Model or dataset
truecourse-ai/truecourse avatar
truecourse-ai/truecourse

TrueCourse's README opens by deprecating the package the repository is named after

Turns the documentation you already write into tests that run. A failing test means your product and your docs disagree, and names the section.

536 stars43 forksTypeScriptMIT

At a glance

What is it?
TrueCourse reads documentation a team already writes, derives claims and user flows from it, and generates executable tests against the real product, so a failing test points at a section rather than a stack trace. It is also a project in the middle of changing shape: the command line package is declared deprecated in the first warning block, the licence splits at one directory, and the three releases in four days are all patch bumps below version one.
Who is it for?
The idea is worth stealing even if you never install this: if your documentation makes claims a user can check, a generated test that fails because the product changed is a better signal than a human reviewer noticing the same drift a quarter later.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 9, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The first warning block deprecates the package the repo is named for

The README does not open with what the tool does. It opens with a warning that the package on the registry, the command line interface, is deprecated and no longer maintained, and that the project is becoming an environment for product owners instead. It then adds the sentence that reframes everything after it: this file describes the product as it is now.

The mechanism underneath is the part worth reading twice. Documentation a team already writes, be it product requirements, decision records, a repository readme or a documentation site, is curated into a corpus of claims. From those claims the flows a user takes through the product are worked out. A test is written for each flow against the real interfaces, and when one fails, the failure names the documentation section that disagrees with the product. Nothing in that pipeline needs a command line, which is a fair explanation for why the command line went first.

The release history points the same way. Three tagged versions landed four days apart, and all three are patch bumps on a line still below one. Nothing about that is a criticism; it is what an interface being rebuilt in public tends to look like.

MIT everywhere except one directory

The licence field in the root manifest says MIT, flat, with no qualifier. The licence section of the README says something narrower: MIT for everything outside one directory, and a separate enterprise licence for the contents of that directory, which it names as holding the document connections, the extra repository providers, and multiple workspaces.

Those three are the difference between a single workspace and a team, and between documentation you paste in and documentation you connect from somewhere else. The enterprise terms are free to read and modify for development and need a subscription for production use, with a second licence file sitting inside that directory.

So the project is open in the sense that matters for reading it, contributing to it and running it for yourself, and closed in the sense that matters for running more than one workspace. The manifest field tells a reader none of that, and a licence scanner pointed at the repository would report MIT and stop there. The root package is also marked private, which is the other place a scanner would be misled.

One sentence about your product gates everything

The first stop in the flow is not connecting a repository. It is a settings page where you say what your product is, in one sentence. Documents are then kept or dropped depending on whether they describe that product, and nothing at all happens until the workspace has been told: no repository connection, no documentation source, no scan.

That is a sharper design than it looks. A documentation-driven test generator inherits every stale document in a company wiki, so the curation step has to be able to reject something, and gating it behind a single sentence means the filter is a relevance decision made once by a person rather than an unbounded crawl.

The cost is that the tool can look broken on a first run. An unconfigured workspace produces nothing at all rather than an error, and the file does not warn about that specific shape of failure, so an empty dashboard is the expected state rather than a broken one. Anyone who knows to write the sentence first will never notice; anyone who does not will spend an afternoon on it.

Two ports, and the tests come from a model you already pay for

The local setup is four commands after copying an environment file, and one more to start it. Two details in there deserve a pause.

First, the ports. The start command is documented as serving on one local port, while the integration endpoint is registered against a different one, and the authentication redirect in the environment example points at the second as well. The dashboard and the server are therefore separate processes, and only one of them is named in the place you would look first.

Second, what actually writes the tests. The file says the whole thing runs on your existing local login to a coding assistant, that it needs that binary on your path and signed in, and that everything runs on one named model unless you change it in the environment file. So the generator is a model you are already paying for, reached through a local tool rather than an API key.

bash
cp .env.example .env
echo "TRUECOURSE_MODE=local" >> .env
echo "TRUECOURSE_LLM_TRANSPORT=claude-code" >> .env
docker compose up -d    # starts Postgres; skip if you already run one, and set DATABASE_URL in .env to it
pnpm install
bash
pnpm dev    # http://localhost:3000

The integration endpoint is registered the same way, as a streamable HTTP transport pointing at the server's own path rather than the dashboard's:

bash
claude mcp add --transport http truecourse http://localhost:3001/mcp

One thing that endpoint buys you is a read surface over the whole workspace from inside your assistant: documents, conflicts, flows, runs, failures, coverage, dependencies and sources, which is the list the file gives for what becomes readable.

Two required settings, and a database only this machine can reach

Two settings are required for every deployment and the server refuses to boot without both. One is the database URL. The other is a master secret of thirty-two characters or more, and what it protects is the interesting part: it encrypts each workspace's saved model provider key, and separately each repository's dependency overlays. One key, two quite different payloads, one of them a credential you will want to rotate and the other plain project state.

The two places that tell you how to generate that secret suggest different commands, an OpenSSL one in the setup section and a Node one in the environment example's comment. Both produce a long random string, so nothing breaks either way, but it is the kind of small mismatch that tells you the prose and the example file were maintained by different routes.

The database that URL points at comes from a compose file shipping a single service, Postgres 16, with a named volume and a readiness check on a five second interval. That file states its own security posture in a comment: the port is bound to the loopback interface, so the database is reachable from this machine and nowhere else, and that binding is the whole of its security here. Candid, and it means the shipped defaults carry more weight than they would in a throwaway stack. The defaults are the project name repeated three times, for the user, the password and the database, and the connection string in the environment example uses the same word. Overriding any of the three is documented, with the requirement to point the application string at whatever you chose.

The image pins a patch version and names the CI provider it apologises to

The container file is two stages on the same exact base image tag, pinned to a patch release rather than a major line, and the comment gives the reason: a floating tag would silently drift the container away from the version continuous integration runs. That is the right instinct, and the cost is that two things have to be bumped together, the base image tag and the node version file at the repository root.

The same file explains why it does not use a build cache mount, on the grounds that the hosted task runner it builds under does not support that syntax and the cache would not survive between builds anyway. It also copies the built client into the server's output directory by hand, because the server serves static files from a fixed path there and nothing else would put them in the right place.

Package management is pinned too, through the manifest's package manager field and a corepack enable step rather than a globally installed binary. The builder stage installs a native compilation toolchain for dependencies that need node-gyp, and the runtime stage is the same base tag again. Nothing in the file is mysterious: every constraint it works around is written on the line above the workaround, which is more than most container files manage.

Eleven internal packages, all declared as development dependencies

The root manifest is private, and its entire dependency list sits under development dependencies. That includes eleven internal packages the workspace resolves to local paths by name: the agent loop, the core, the data store, the database layer, the GitHub app, a guard generator and a guard runner, the job system, the model interface, and a shared package.

Those names describe the architecture better than the prose does. The guard pair is the informative one, because a generator and a runner for the same artefact imply that generated files are treated as something to check rather than as an end in themselves, which matches the promise that a failing test names a disagreeing section.

Among the third-party entries are two database technologies, the containerised Postgres used at runtime and an in-process one used for tests, plus a schema validator and a regular expression denial-of-service detector. That pairing is a sensible thing for a tool whose input is whatever a team has already written down. A telemetry client, an error reporter and a session cookie library sit in the same list, which is consistent with the local, hosted and self-hosted modes sharing one codebase rather than three.

The scripts at the top are the usual monorepo set, delegating to a task runner for build, lint and typecheck, with the test command calling the test runner directly at the root rather than through the task graph.

Pull request checks need write access, and ship switched off

The integration piece is a GitHub app that checks pull requests for documentation conflicts and test failures, then reports the results in GitHub. It needs two permissions: read on pull requests, and read and write on checks, which is what lets it post a check result at all. It also subscribes to three event types, and the feature is off by default, enabled per repository in the repository's settings.

That combination is the right shape for something that reads your documentation and writes verdicts onto your commits, and the file states it plainly rather than burying the permission in a setup guide. The read half is what lets it inspect the pull request; the write half is what lets it answer. A tool that generated a test failure you already knew about would be a curiosity, and one that could mark a pull request on its own judgement deserves the wider permission and a default of off.

Telemetry is the last thing worth naming. Usage analytics go to a third-party product, covering which actions are taken and page views, and the file says explicitly that documents, keys and tokens are never included, with a single environment variable to switch the whole thing off. Both the permission and the telemetry choice are disclosed in the place someone configuring the app will actually look, rather than in a separate policy page.

Editorial conclusion

The idea is worth stealing even if you never install this: if your documentation makes claims a user can check, a generated test that fails because the product changed is a better signal than a human reviewer noticing the same drift a quarter later. What you inherit by installing is a test generator that runs on a model you pay for separately, since the local setup is built on a signed-in command line login, with hosted mode as an alternative that additionally needs a third-party identity provider's credentials before the server will boot. Before you rely on it, read the licence split rather than the licence field, because one directory holding the document connections is under commercial terms, and read the first warning block too, because the command line tool the project is named after is no longer maintained.

Frequently asked questions

What is truecourse-ai/truecourse?

It is a TypeScript project that reads documentation a team already writes, curates it into claims, derives the flows a user takes through the product, writes a test for each flow against the real interfaces and runs them. A failing test means the product and the documentation disagree, and it names which section.

Is the truecourse command line tool still maintained?

No. The first warning block states that the package is deprecated and no longer maintained, and that the project is becoming an environment for product owners instead. The rest of the file describes the product as it is now, and the repository's root package is marked private.

What license does truecourse use?

The manifest records MIT, but the README splits it: MIT for everything outside one directory, and an enterprise licence for that directory's contents, which hold document connections, extra repository providers and multiple workspaces. Those are free to read and modify for development and need a subscription for production use.

What does truecourse need to run locally?

An environment file copied from the example with two mode settings, a Postgres container or an existing database whose URL you set, a package install, and one start command. You also need a coding assistant command line binary on your path and signed in, because everything runs on that login, on one named model unless you change it in the environment file.

What are the two settings every truecourse deployment requires?

A database URL and a master secret of thirty-two characters or more. The server refuses to boot without both. The secret encrypts each workspace's saved model provider key and each repository's dependency overlays, so one value covers two different kinds of state.

What permissions does the truecourse GitHub app need?

Read on pull requests and read and write on checks, which is what lets it post a check result, plus subscriptions to the pull request, check run and check suite events. Pull request checking is off by default and is enabled per repository in settings.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. truecourse-ai/truecourse on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/truecourse-ai-truecourse.svg)](https://hysenlabs.com/projects/truecourse-ai-truecourse)