Development - andreaderuvo/argus GitHub Wiki
python3 -m pytest -q -n 16 # everything, the browser suite included: under 2 minutes
python3 -m pytest -q -n 16 -m "not browser" # the quick loop: Python only
npm test # the first line
npm run test:quick # the second
npm run lint # ESLint on static/
npm run relink # regenerate the frontend modules' import blocks
npm ci # once: ESLint and the parser the checks use-n is pytest-xdist (in requirements.txt): each
worker starts its own Argus, port, temp HOME, tmux socket and Chromium, so the browser suite runs
in parallel safely โ 12 minutes on one worker, under 2 on sixteen. CI uses four.
The count changes most weeks; pytest is the truth. Node is used for checks only โ the
app itself has no build step and no dependencies, and without npm ci those checks skip rather
than fail (CI sets ARGUS_BROWSER_REQUIRED=1, which turns a missing browser or linter into a
failure, so a runner that lost one cannot pass by testing nothing).
The tests never touch tmux's default socket. Neither should anything else you run against this code: a tmux server that dies takes every session on it with it, which is how this repository learned the rule the hard way.
The tooling, since a folder of unexplained scripts is worse than no folder:
scripts/openapi.py |
rewrites docs/openapi.json from the code. The test suite runs it with --check and fails if the published description has drifted, so this one is not optional. |
scripts/demo.py |
builds the instance every screenshot and clip is made from โ fabricated sessions, fabricated files, on its own tmux socket. It exists so the pictures can be remade when the interface moves, and so that "none of this is a real machine" is something you can verify rather than take on trust. |
scripts/orchestra.py, scripts/referee.py, scripts/fullstack.py
|
three worked orchestrators, each run against real tmux before being committed โ see Orchestrating several agents |
tools/argus_client.py, tools/argus-say
|
the Python client and a command-line front door onto it: the transport. Standard library only, one file, no package โ see An agent driving Argus. |
tools/argus_orchestra.py |
the orchestration framework on top of it โ the contract, the waiting, the names, the worktrees. It is why the three examples are twenty lines each: see Writing an orchestrator. |
scripts/surface.py |
lists every public name in tools/ and fails if one has no docstring, or โ given a checkout of this wiki โ if one is named nowhere on it. The test suite runs both; CI clones the wiki so the second one is real there rather than skipped. It exists because the client was once documented eight methods out of fourteen and nothing noticed. |
scripts/record.mjs |
drives a Chromium over the debugging protocol to record those clips, from the demo instance. |
scripts/modules.mjs |
npm run relink: regenerates the import block at the top of every frontend module from what the module actually uses. Also moves code between modules without breaking it โ see below. |
scripts/apiref.py |
writes this wiki's API reference from docs/openapi.json (itself from scripts/openapi.py): python3 scripts/apiref.py ~/argus.wiki/API-reference.md. tests/test_wiki.py fails when it is not current, when a wiki link or #anchor goes nowhere, or an image points at a branch that does not exist โ with the wiki cloned at ~/argus.wiki; skipped otherwise. |
tests/browser/ |
the browser suite: a real Argus driven by a real headless Chromium. See below. |
Plain ES modules in static/js/, no
bundler: edit, reload, done. It was one 16,600-line app.js until September 2026, split one
section per module with the behaviour unchanged and the browser suite green at every step.
app.js is still the entry point โ one import โ because the page, the service worker and
installed PWAs all know it by name; js/main.js is the start-up and imported by nothing else.
Three rules, each learnt during the split, and each checked by a test:
-
The imports are generated. Every module starts with a
// <imports>block listing what it uses from the others. Do not edit it: write the code, runnpm run relink. ESLint says when a name is used but not imported (no-undef), or when an imported variable is being assigned (no-import-assignโ an ES import is read-only, so shared state has a setter in the module that owns it). A stale block fails the tests. -
Load order is not file order. The sections use each other in circles, so a module's
top-level code can run before a module it reads from is ready โ "Cannot access 'prefs'
before initialization", and a page that never starts. Code that runs at load may only read
another module's variables if that module is in no import cycle (
state.js,dom.js,words.js,icons.jsโฆ).tests/js/loadorder.mjsfollows the calls, across modules, and names the whole chain when it finds one. - Every module is in the service worker's list, or an installed app breaks offline; and a module that is there for what it does at load (taking the token from the address) needs an import of its own, since nothing uses its exports.
tests/browser/ starts Argus as a
person would, with a config of its own, a folder of sample files, its own HOME and a tmux
socket named argus-t-โฆ โ never the default one โ and drives it with headless Chromium over the
debugging protocol, at desktop and phone size. Every screen, and the things people do: a shell
from an empty session list and typing into it, rename and kill with and without confirming, a
markdown report with its figure (and its <script> neutralised), hidden files, theme, language,
desks, labelling a process.
Any problem fails the test by itself โ an uncaught exception, a console error, a request of ours answering 4xx or 5xx, a module answered with HTML because the file is missing โ so a test written to check one button also catches the error it set off somewhere else. It was checked against itself: bugs planted on purpose (a misspelt name in a rare path, a screen that throws, kill without confirmation, a broken figure URLโฆ) all fail it. A terminal is checked by what a typed command does, never by reading the screen back.
Chromium is found in $ARGUS_CHROMIUM, then Playwright's cache, then the PATH.
Vendored under static/vendor/, unmodified, each keeping its own copyright header:
- xterm.js 6.0.0 โ MIT
- marked 18.0.7 โ MIT
- qrcode-generator 2.0.4 โ MIT
- highlight.js 11.12.0 โ BSD-3-Clause, the ESM build: the core and 27 languages, imported only when a file of code is opened
- pdf.js 4.10.38 โ Apache-2.0, the build Argus draws every PDF with
-
swagger-ui 5.32.14 โ Apache-2.0, the bundle and
its stylesheet, served at
/api/docs; FastAPI's own one-liner would fetch it from a CDN -
three.js 0.185.1 โ MIT, the module build with
OrbitControlsandSTLLoader, for the 3D viewer -
occt-import-js 0.0.23 โ LGPL-2.1, the OpenCascade
kernel compiled to WebAssembly, loaded only to read a STEP file. It has its own
NOTICE.mdexplaining the licence - mermaid 11.16.1 โ MIT, the minified ESM build with its chunks and without its source maps: 3.7MB on disk and none of it fetched until a document with a ```mermaid fence in it is opened, at which point the entry and the chunks for that one kind of diagram are what load
-
diff2html 3.4.56 โ MIT, the UI bundle without its
highlighter (Argus's highlight.js is handed to it) and its stylesheet: the built-in
diffviewer pack, which draws.diff/.patchfiles and the Changes window -
Material Icon Theme 5.39.0
โ MIT, the file icons only (633 SVGs, 512 KB) and their extension and file-name map, cut from
the package by
scripts/fileicons_vendor.py; each loaded when a file of its kind is listed. The genomics ones (sequences, alignments, variants, trees, annotations, pipelines) are Argus's own, instatic/img/fileicons/
Optional at runtime: pandoc for Word documents, and pdftotext
(poppler) to search inside PDFs. Without either, those files still open โ they are just
plainer.