Skip to content

Latest commit

 

History

56 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

JobTrack - hierarchical job-tracking and costing

JobTrack banner

A hierarchical job-tracking system with dynamic, historically-accurate costing.

Live Google Cloud Run demo with login demo and password demo-jobtrack-1234. Note the instance may take a couple of seconds to warm up when stopped, and recycles when not in use (the database is reset to a default state).

Introduction

This is a recreation of my original project running on SQL Server and .NET 4.8, now rebuilt from scratch on PostgreSQL (or SQLite) and .NET 10, and packaged as a docker image for easy deployment.

The app has 2 unusual features:

  • work is organised into branches and leaves, with a single root node; the tree can be arbitrarily deep, and any node can be moved to a new parent without losing its history or breaking its cost calculations. Actual work is done in leaves, which can be paused and resumed, and worked on by several people concurrently. See this tree image
  • cost is computed live, according to work schedules (with overrides), and concurrent work is split fairly across all participants. See costing-engine.md and rate-resolution.md. Job cost is never stored, and can be recomputed at any time for any node in the tree, even if the tree has been restructured since the work was done.

Additionally, all jobs can have prerequisites, and the system will automatically prevent work from being started on a node until all its prerequisites are complete. As with other features, this applies to both branches and leaves. Since branches don't have work themselves, a branch is considered complete when all its leaves are complete.

Two database backends are supported:

  • PostgreSQL is the production backend, and is used in the live Google Cloud Run deployment. Supports multi-instance concurrent writes from multiple web hosts.
  • SQLite is a fully conforming second provider, intended for embedded and demo use. Writes are always serial in SQLite by design, and we use WAL to ensure writes don’t block concurrent reads.

Overview

  • Stack: .NET 10, C# 14, EF Core 10, Noda Time, ASP.NET Core Identity, Postgres/SQLite.
  • Shape: a database and a reusable library, with three clients over them — see Architecture.
  • Two databases, one behaviour: PostgreSQL is the production backend; SQLite is a fully conforming second provider for embedded and demo use, held equivalent by a shared contract-test suite.
  • Performance: hundreds of thousands of jobs, and years of history have been planned for. Budgets and evidence: docs/traceability/performance-budgets.md.
  • Security: from first principles, defence-in-depth hardening, split least-privilege credentials, audited administrative actions, two-factor authentication, and a maintained threat model with every mitigation tied to a named test: docs/threat-model/web-authentication-threat-model.md.

Leaf statuses

Every leaf shows one status. The short form appears where space is tight (a phone, a dense tree row); the full word appears elsewhere. Full reference: docs/behaviour-overview.md.

Status Short Meaning
Active (time) / N active Someone is working it right now
Waiting Wait Open, not worked yet — Start begins the first session
Paused Paused Started, nobody clocked on right now — Start resumes it
Unack Unack A requester's submission staff have not accepted yet
Success Succ Finished successfully (the only outcome that satisfies a prerequisite)
Cancelled Cancel Withdrawn without completing
Unsuccessful Unsucc Attempted but did not succeed
Closed Closed Archived

A leaf whose prerequisites are unmet also carries a blocked marker over its status; work cannot start until they reach Success.

Branches carry no leaf status of their own — their status is computed from the leaves beneath them (recursively) and collapses to two states:

Status Meaning
Success Every leaf in the branch has succeeded
Unfinished At least one leaf has not succeeded yet (the ordinary in-flight state)

Architecture

More details: docs/architecture-overview.md.

Ports and adapters (hexagonal), close to Clean Architecture with two departures: the database is a layer that enforces its own invariants, not a detail hidden behind a repository; and the library exposes one coarse facade, not an interface per use case.

Five layers, built and depended on strictly bottom-up. The database and library stack; the three clients above them are siblings, each calling IJobTrackClient in-process — the web client does not go through the HTTP API:

  1. Database — PostgreSQL (production) or SQLite (eg embedded and demo use) as numbered forward-only SQL scripts applied by JobTrack.Database.
  2. Reusable library — the domain, use cases and both EF Core persistence providers, behind the single IJobTrackClient facade; persistence is inverted, so each provider implements ports the application layer declares.
  3. HTTP API — a token-authenticated versioned JSON API. This is for external clients; the web client does not need it. A future mobile app or SPA would connect here.
  4. Web client — mobile-friendly as a first principle. Server-rendered Razor Pages rather than an SPA, to maximise compatibility: it works without client-side state on legacy browsers, and is intentionally conservative.
  5. Admin CLI — uses the same library, to bootstrap the first administrator, create employees, emergency password and 2FA resets, and job-tree import.

The HTTP API and the web client share the one JobTrack.Web process; the admin CLI is its own executable. Each process picks a database provider at startup and then reaches the database only through IJobTrackClient — the dependency rules are asserted by the tests in tests/JobTrack.ArchitectureTests/.

Key documents

If you want to… Read
Build, test, run, or administer it locally docs/developer-guide.md
Understand how it behaves for its users docs/behaviour-overview.md
See the architecture and layers file by file docs/architecture-overview.md
Deploy or operate it docs/operations/postgresql-cloud-run-deployment.md

Code size

Lines of code as counted by tokei (blank lines and comments excluded), as of 19 August 2026:

Area Files Lines of code
Product — src/ 733 38,016
Tests — tests/ 445 72,854
Database schema — database/ 43 2,399
Sample API client — samples/ 19 1,171

The 72k lines of test code are a consequence of Test Driven Development, with over 4,000 tests in the full suite. It takes up to 10 minutes to run, even on a fast Mac.

Files are held to hard size ceilings by an architecture guard: 1000 code lines for product and sample C#, 2000 for test C#, and 500 physical lines for Razor. A C# code line is one carrying at least one token, so comments and blank lines are free. Methods are capped at 75 executable lines.

A short test script, aiming to complete in about 20 seconds, is used for pre-commit checks scripts/fast-test.sh

Status

Current release: v1.2.0 (2026-08-24) — merges the Active column's Unstarted leaf status into Waiting (ADR 0070), since starting an unstarted leaf auto-attaches its work record and the two read identically to a user, and adds an end-user leaf/branch status reference. No schema or contract changes. Full history: CHANGELOG.md.

Release-ready. All four delivery gates — database, reusable library, web application, and release — have formal, source-controlled acceptance records (ADR 0025, 0026, 0027, 0063). The codebase was built test-first throughout (about 1.9 lines of test for every line of product code), passes its full solution and performance suites, and has been through three internal security audits, each fully remediated. Performance is enforced: measured budgets on a 200,000-node production-shape database run as regression ceilings on every performance-suite run.

The production deployment is PostgreSQL on Google Cloud (Cloud Run + Cloud SQL, with automated backups and point-in-time recovery), defined by ADR 0062.

Live Google Cloud Run demo (SQLite backend — a demonstration configuration, not production).

The SQLite backend can be run in a throwaway docker container or as a persistent local database. See docs/operations/sqlite-limitations-and-configuration.md for its limitations.

Documentation map

Design and specification

Operations, security, and traceability

License

AGPLv3. See LICENSE and THIRD-PARTY-NOTICES.md.

About

Hierarchical job-tracking system with dynamic, historically-accurate costing

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Used by

Contributors

Languages