Skip to content

docs(trains,machines,dispatch,surface,tooling): agent skills for apps built on Trax - #103

Open
Theauxm wants to merge 11 commits into
feat/ms16-agent-skillsfrom
feat/ms16-skills
Open

Theauxm wants to merge 11 commits into
feat/ms16-agent-skillsfrom
feat/ms16-skills

Conversation

@Theauxm

@Theauxm Theauxm commented Oct 20, 2026

Copy link
Copy Markdown
Member

A coding agent in a Trax app knows what to decide first, which page to read and which mistakes are known

Merge after #102, which this branch is stacked on.

Background

The previous PR set up where agent skills live (Trax.Docs/skills/) and how a release publishes
them. This one writes them: eleven skills, one entry point and ten for the tasks people building on
Trax actually do.

A skill here is a router plus a procedure plus the traps. It says when it applies, what to decide
before writing code, the steps in order, the known mistakes with the wrong and right form, and links
to the docs page that holds each detail. It does not copy the docs.

Problem

⬛ An agent new to Trax guesses, and the docs do not stop it
A trial build of a card game on Trax, by an agent with only nuget.org and traxsharp.net, had the
docs and still got the order wrong. It deleted the template's sample trains before reading how to
lay out a train, so every train broke the layout. It met three machine design rules in reference
tables after building a machine that broke them, and rewrote it. It wrote Rule.Custom(name) for
new Rule.Custom(name), threw InvalidOperationException where only a plain TrainException
reaches a GraphQL caller unmasked, picked trax-api where a machine that invokes trains needs
trax-hub, and found namespaces by searching the packages' XML docs. Each of those is a sentence
in the right place.

🟪 Some rules exist only in code
Writing the machine skill turned up rules the docs do not state, or state loosely: one of the three
user-owned-machine rules is checked at host startup, not by Build(); an outcome may enter the start
state when the start state itself invokes a train; the builder refuses an effect on an invoking state.

Example

The trial's train, BeginRun, as an agent writes it without and with trax-write-a-train:

Without With
Files input, output, interface, train and junction in BeginRunTrain.cs Trains/BeginRun/ with one type per file, Junctions/ and Models/
Junctions public internal; the rest of the app talks to IBeginRunTrain
Refusing a request throw new InvalidOperationException("Run already started"); the browser shows Unexpected Execution Error throw new TrainException("Run already started"); the browser shows the message as TRAX_TRAIN_ERROR
Shared code a RunJunction<TStep> base class before a second train needed it share late, only a generic junction, in Trains/Shared/Junctions/

The skill's example is one train, its input, output, interface, an internal model and two internal
junctions, compiled in CI against this checkout:

internal class PriceOrderJunction(ICatalog catalog)
    : EffectJunction<PlaceOrderRequest, PricedOrder>
{
    public override async Task<PricedOrder> Run(PlaceOrderRequest input)
    {
        if (input.Quantity <= 0)
            throw new TrainException("Quantity must be at least 1.");

        var price = await catalog.PriceOf(input.Sku, CancellationToken);
        return new PricedOrder(input.Sku, input.Quantity, price * input.Quantity);
    }
}

How this solves it

⬛ One entry skill routes to the rest
trax loads for any task in a repository that references Trax. It checks that the skills' stamped
trax-version matches the app's packages (and says what to do when it does not), states the rules
that hold everywhere (one version for every package, Trax's own Either and Unit, read the
startup refusal before changing anything, never open a closed default), the package map, the four
pages to read first, and a table from task to skill.

flowchart LR
  E["trax"] --> S["trax-start-a-project"]
  E --> T["trax-write-a-train"]
  E --> D["trax-write-a-decider"]
  E --> M["trax-design-a-machine"]
  E --> A["🟥 trax-expose-over-api"]
  E --> C["trax-generate-a-client"]
  E --> W["trax-schedule-work"]
  E --> X["trax-test-your-app"]
  E --> U["trax-upgrade"]
  E --> F["trax-troubleshoot"]
  class E,S,X,U,F tooling
  class T,D trains
  class M machines
  class A,C surface
  class W dispatch
  classDef trains fill:#22c55e,stroke:#15803d,color:#000
  classDef machines fill:#a855f7,stroke:#7e22ce,color:#fff
  classDef dispatch fill:#f97316,stroke:#c2410c,color:#000
  classDef surface fill:#3b82f6,stroke:#1d4ed8,color:#fff
  classDef tooling fill:#334155,stroke:#0f172a,color:#fff
Loading

🟩 Trains and decisions
trax-write-a-train: ServiceTrain or Train, EffectJunction or Junction, the layout, the
chain and Memory, registration, which exception a caller sees, cancellation. trax-write-a-decider:
when a choice is a decider and when an if is fine, tracks, confidence bars, shadow, replay,
checkpoints.

🟪 Machines, rules first
trax-design-a-machine puts train-or-machine first, then every design rule before a state is
drawn, each marked with whether Build() or host startup refuses it. Its example machine builds
and exports its IR against this checkout, and each wrong variant was built to confirm the refusal.

Caution

🟥 trax-expose-over-api teaches the closed defaults and nothing else.
Every surface takes a posture, demo credentials stay inside IsDevelopment(), and a refusal is read,
not worked around. Making a surface public, running unregistered documents, admitting an origin in
production, returning exception detail, opening operations or choosing production credentials is
written as "ask the user", with what it exposes, and no method name to do it.

🟦🟧⬛ The rest
trax-generate-a-client (the manifest export, trax client, documents, driving a machine from
TypeScript and React, Origin in a browser against Node); trax-schedule-work (manifests, the
queue, option ranges inline, what each data provider supports); trax-test-your-app (the testing
packages, the guards, and a compiled reflection test for what no guard checks: internal junctions);
trax-start-a-project; trax-upgrade (every migration guide with the release it applies from);
trax-troubleshoot (message to page, reading a run's record, what to collect before asking).

⬛ Held in step with the docs
Every compile fence builds against this checkout, every docs link resolves to a published page and
heading, and SkillsCoverMigrationGuidesTests fails when a migration guide is added without a line
in trax-upgrade.

Checked: the Trax.Docs suite and ADR guard green; the eleven staged at a release version pass
claude plugin validate --strict and list and install with the skills CLI, while this checkout
offers none. Whether they change what an agent does is the next step: the same trial brief, with and
without them.

Architectural hinges

  • ⬛ Trax.Docs/skills/trax/SKILL.md: the version check, the rules every other skill assumes, and
    the routing table; read it first.
  • 🟪 Trax.Docs/skills/trax-design-a-machine/SKILL.md: the skill with the most behaviour the docs
    did not state, checked against the builder.
  • ⬛🟥 Trax.Docs/skills/trax-expose-over-api/SKILL.md: the security posture an agent is taught.
  • ⬛ Trax.Docs/tests/Trax.Docs.Tests/Tests/SkillsCoverMigrationGuidesTests.cs: the one new guard;
    ties trax-upgrade to migration-guides/.

@vercel

vercel Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
trax-website Ignored Ignored Preview Oct 20, 2026 8:06pm UTC

@Theauxm
Theauxm added this pull request to stack #104 October 20, 2026 19:29

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant