roadmap-core
The roadmap graph: status derivation, dependency and relation edges, arc state,
validation, and the markdown renderers behind roadmap/ROADMAP.md and ARCS.md.
Stdlib-only and dependency-free, which is the point rather than a nicety. Three properties depend on it:
scripts/roadmap.pyloadsroadmap_core/graph.pyby path, so a coding agent in a checkout can read the backlog with no install, no DB, no admin token and no network.- The Lucille backend imports the same module, so a status derived by the API and a status rendered into the committed markdown cannot disagree — one implementation, two callers. That divergence is a failure this repo has been bitten by before.
- It is the extraction seam. The roadmap is being prepared to run as its own product whose default store is a single SQLite file with nothing to provision; a package that pulled in a web framework or an ORM could not be adopted by another repo without adopting Lucille with it.
Storage
store.py is the schema — one SQLite file, CREATE TABLE IF NOT EXISTS on first
open, no migration step. stores.py is what you talk to:
LocalStore |
ApiStore |
|
|---|---|---|
| needs | a writable path | a call(method, path, payload) |
| provisioning | none | a running host |
claim/release |
one BEGIN IMMEDIATE transaction |
one HTTP request |
impact |
raises Unsupported |
the host's tickets |
Both satisfy the same Store protocol, so a caller never branches on which it
holds. ApiStore is constructed with the host's own caller and holds no token,
no URL and no urllib import — auth stays entirely a host concern, and
test_stores.py asserts that rather than trusting it.
from roadmap_core.stores import LocalStore
with LocalStore("roadmap/roadmap.db") as store: # created if absent
store.upsert_item({"key": "a-thing", "title": "A thing"})
store.claim("a-thing", by="claude/some-branch")
From the CLI, --source local on push, claim, release and status, or
ROADMAP_SOURCE=local once. ROADMAP_STORE sets the path.
Adopting it in another project
Measured end to end by tests/test_adoption.py, which runs the CLI as a
subprocess against a scratch project with nothing on the path but this package
— no backend, no FastAPI, no SQLAlchemy, no Postgres, no server, no token.
pip install "roadmap-core[files]" # [files] adds PyYAML, which authoring needs
mkdir -p roadmap/items
export ROADMAP_SOURCE=local
That is the whole install. roadmap is a console script that comes with the
package — there is nothing to copy. (It used to say curl -o scripts/roadmap.py <this repo>/scripts/roadmap.py, and this repo is private, so the package was
installable, importable and useless to anyone outside it.)
Then the ordinary loop, which needs nothing else:
cat > roadmap/items/first-thing.yaml <<'YAML'
id: first-thing
title: The first thing to do
status: ready
evidence: |
Why this is worth doing, and how you will know it worked.
YAML
roadmap push # files -> store
roadmap ready # what is startable
roadmap claim first-thing
roadmap release first-thing
The store is one SQLite file at roadmap/roadmap.db. There is nothing to
provision and no migration to run: it is created on first open.
Two things that are conventions rather than choices, both found by doing this rather than by reading the code:
- Your project root is the nearest ancestor holding
roadmap/itemsor.git, so the commands work from anywhere inside it. SetROADMAP_REPO_ROOTto pin it. Deliberately not bareroadmap/: that is a directory the tool creates, so keying on it let one command run in the wrong place mint the marker that made that place look like a project forever after. - Authoring is writing a YAML file, not calling an API.
pushis what moves it into the store; there is noroadmap new. That is deliberate: filing an item belongs in a diff somebody reviews.
ROADMAP_SOURCE=local selects the SQLite store. Without it the CLI expects the
API store, which is how Lucille runs it — see roadmap_core.stores.
CI
Copy templates/roadmap.yml to .github/workflows/roadmap.yml. That is the
whole CI story for the floor:
push # files -> store, rebuilt fresh each run
validate # schema, dangling dependencies, cycles
sync --check # is the committed ROADMAP.md still what the graph renders?
No schedule, no credentials, no bot identity, no commit back to the default
branch, no self-hosted runner. tests/test_adoption.py reads the commands out
of that file and runs them, so a template that has drifted from the CLI fails
rather than reading as tested.
The third line is the one that earns the workflow. ROADMAP.md is generated but
committed — that is what lets an agent read the backlog with no install and no
network — and a generated file nobody regenerates is a file that lies.
Do not commit roadmap/roadmap.db. It is derived: push rebuilds it from
the YAML on first open, and a binary file in git conflicts on every claim. The
files are the record; the store is the transaction that decides who gets one.
Upgrading to a served store
The floor's simplicity comes from one property: the store is inside the checkout, so there is no second copy to drift from. Move the store to a server — so that claims are visible across machines the moment they are taken, rather than when a branch merges — and four things come back, none of which the template can supply for you:
| What returns | Why |
|---|---|
| A credential step | the store is now behind auth, and the CLI needs a token per run |
| A wait-for-reachable step | a concurrent deploy can hold the store down, and being early is not being wrong |
pull and a bot commit |
the store now knows things no checkout does, and they have to land in the files an agent reads |
| A schedule | finishing an item is usually a code change somewhere else entirely, so no path filter can catch it — only re-asking on a clock can |
ApiStore is constructed with your own caller, so the auth stays yours (see the
table under Storage). Lucille's .github/workflows/roadmap-sync.yml
is the worked example of all four, and the reason it is not shipped as a
template: almost every line of it is a consequence of Lucille's own deployment,
and handing an adopter that machinery for a problem they do not have reads as
required rather than as one option.
What is NOT here
HTTP, auth, and the CLI. The graph is pure functions over plain dicts keyed by
key, so the same code serves DB rows, API payloads and parsed YAML with no
adapter. Lucille's own persistence stays in backend/app/crud/roadmap.py over
SQLAlchemy; the two definitions of the same three tables are held together by
backend/tests/test_roadmap_store_parity.py, which asserts both the columns and
the row dicts the two readers produce.
The package's tests live in tests/ here and import nothing outside the stdlib.
.github/workflows/roadmap-core-tests.yml runs them in a job that fails if
app, fastapi, sqlalchemy or yaml can be imported at all — the isolation
is asserted, not assumed.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file roadmap_core-0.2.1.tar.gz.
File metadata
- Download URL: roadmap_core-0.2.1.tar.gz
- Upload date:
- Size: 90.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fba478f02ed239e686fd57e2ac609b30ad012f1d3cdce05b3fc3a9db28b7578d
|
|
| MD5 |
566bebe51c816e9815774540711d4daf
|
|
| BLAKE2b-256 |
f2ba7506d176ffa52513c6ec7410b703d1117704be6b0dc8b698afacc371ced2
|
Provenance
The following attestation bundles were made for roadmap_core-0.2.1.tar.gz:
Publisher:
publish.yml on gald33/roadmap-core
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
roadmap_core-0.2.1.tar.gz -
Subject digest:
fba478f02ed239e686fd57e2ac609b30ad012f1d3cdce05b3fc3a9db28b7578d - Sigstore transparency entry: 2561561980
- Sigstore integration time:
-
Permalink:
gald33/roadmap-core@2015b488bc6ee63ce1a7ba32fb841bfc5affd0bd -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/gald33
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2015b488bc6ee63ce1a7ba32fb841bfc5affd0bd -
Trigger Event:
release
-
Statement type:
File details
Details for the file roadmap_core-0.2.1-py3-none-any.whl.
File metadata
- Download URL: roadmap_core-0.2.1-py3-none-any.whl
- Upload date:
- Size: 70.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6be218fa3689b4f37b365e4c77c7614344e3e3ce46eb4ccdec951d4bdb70de8a
|
|
| MD5 |
be0c94cd0bb0602cb60d705fe5b89d36
|
|
| BLAKE2b-256 |
965e3c3effcbaf60b13fef164a1b75ae0e5a065a47e8962ee2487736413be1ab
|
Provenance
The following attestation bundles were made for roadmap_core-0.2.1-py3-none-any.whl:
Publisher:
publish.yml on gald33/roadmap-core
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
roadmap_core-0.2.1-py3-none-any.whl -
Subject digest:
6be218fa3689b4f37b365e4c77c7614344e3e3ce46eb4ccdec951d4bdb70de8a - Sigstore transparency entry: 2561562256
- Sigstore integration time:
-
Permalink:
gald33/roadmap-core@2015b488bc6ee63ce1a7ba32fb841bfc5affd0bd -
Branch / Tag:
refs/tags/v0.2.1 - Owner: https://github.com/gald33
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2015b488bc6ee63ce1a7ba32fb841bfc5affd0bd -
Trigger Event:
release
-
Statement type: