Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 28 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,66 +13,61 @@

---

## 📖 The Philosophy: Why This Project Matters
## The philosophy

The **Ship of Theseus** is a famous thought experiment: if you replace every wooden plank on a ship over time, is it still the same ship?
The Ship of Theseus is a thought experiment: if you replace every wooden plank on a ship, is it still the same ship?

This exact paradox plays out daily in modern software engineering. Repositories live for years, or even decades. The developers who started them leave, entire architectural paradigms shift, and eventually, the very last line of original code is overwritten. Yet, the repository retains its name, its URL, and its identity.
Software projects do this constantly. A repository can live for decades. The original developers leave, architectures shift, and eventually, the last line of the original commit is deleted. But the repo keeps its name and URL.

**This project exists to visualize that journey.** It pulls back the curtain on repository decay and renewal by measuring *codebase entropy*; tracking when lines of code were written and how long they survive before being rewritten, effectively showing you the "age" of a massive software project at a glance.
This project visualizes that process. It measures codebase churn by tracking when lines of code were written and how long they survive.

### Why People Care About This
1. **Repository Health & Churn Visibility:** Open-source maintainers and engineering managers can visually assess how quickly a codebase is turning over. Is the core architecture stable (lots of old code), or is it undergoing a frantic rewrite?
2. **Identifying Key Surviving Code:** By identifying "Historical" and "Living" fossils, this project highlights the original architectural foundation blocks that have stood the test of time (and edge-cases).
3. **Data-Driven Storytelling:** It acts as a historical lens for famous open-source projects, allowing developers to see how massive frameworks (like React or Django) have evolved through different eras.
People use this to:
- See how quickly a codebase is turning over. A stable architecture holds onto old code, while a frantic rewrite shows a sudden drop.
- Find the "fossils" — the oldest surviving lines of code that somehow escaped refactoring.
- Look at the history of frameworks like React or Django to see exactly when major rewrites actually shipped.

## QuickStart Guide
## Setup

### 1. Requirements
* `git`
* `python` > 3.12
* `poetry` (for dependency management)
You will need `git`, `python` (3.12+), and `poetry`.

### 2. Installation
```bash
git clone https://github.com/Asifdotexe/Theseus.git
cd Theseus
poetry install
```

### 3. Running the Engine Locally
The analytical engine is driven through the centralized `theseus.config.json` configuration file.
You can run the full timeline snapshot engine:
### Analyzing a repository

The script reads from `theseus.config.json`. To run a full analysis:

```bash
poetry run python scripts/analyse_repository.py
```

To backfill or incrementally update the "Fossil" pointers (the absolute oldest lines of code):
To update the pointers to the oldest surviving lines of code (the "fossils"):

```bash
poetry run python scripts/add_fossils.py --update-survivor
```

### 4. Viewing the Interactive Chart
Simply open `index.html` in your favorite modern browser:
### Viewing the chart

Open `index.html` in a browser.

```bash
# On Mac
# macOS
open index.html

# On Windows
# Windows
start index.html
```

---
## Documentation

## Dive Deeper (Documentation)

The technical internals of the Ship of Theseus engine are separated into structured documentation guides:

- **[Architecture & The Data Pipeline](docs/ARCHITECTURE.md):** How we traverse `git` histories incrementally and capture "Fossils".
- **[Configuration Guide](docs/CONFIGURATION.md):** How to plug in your own repositories by editing `theseus.config.json`.
- **[DevOps & CI/CD](docs/DEVOPS.md):** How the system updates itself autonomously via GitHub Actions.

---
- [Architecture & Data Pipeline](docs/ARCHITECTURE.md): How the script traverses git history and captures fossils.
- [Configuration Guide](docs/CONFIGURATION.md): How to analyze your own repositories.
- [DevOps & CI/CD](docs/DEVOPS.md): How the GitHub Actions pipeline runs updates automatically.

## License
This project is open-source and available under the terms defined in the `LICENSE` file.

This project is available under the terms defined in the `LICENSE` file.
Binary file added assets/theseus-og-picture.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
48 changes: 21 additions & 27 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
# Architecture & Internals
# Architecture & internals

The Ship of Theseus engine is composed of a disconnected backend (data generator) and frontend (UI visualizer). They communicate entirely via an intermediary static JSON format.
The project has a Python script to generate data and a web frontend to display it. They communicate via static JSON files.

This architecture allows the system to remain highly secure, completely serverless, and free to host using static GitHub Pages. (woohoo, who doesn't like free things?)
This setup allows the site to be hosted on static platforms like GitHub Pages.

---
## The data pipeline (`analyse_repository.py`)

## The Data Pipeline (`analyse_repository.py`)
The data generator runs `git` shell commands directly instead of using Python libraries like `GitPython` or `pygit2`. This is significantly faster because the native `git` binary is optimized in C.

The heart of the application is a python script that orchestrates `git` shell commands. Because `git` is heavily optimized in C, shelling out to the native git binary is orders of magnitude faster than relying on pure Python implementations like `GitPython` or `pygit2`.
### Incremental snapshot generation

### Incremental Snapshot Generation

To view codebase health *over time*, we need snapshots of the codebase. Instead of re-parsing every commit since the dawn of time, the engine works incrementally.
The script needs snapshots of the codebase over time. Rather than parsing every commit, it works incrementally.

```mermaid
flowchart TD
Expand All @@ -28,19 +26,17 @@ flowchart TD
I --> J[Append Snapshot to JSON]
```

### The `git blame` Parallelization

When checking out a specific month's commit, the system needs to `git blame` every single valid file in the repository.
### Git blame parallelization

1. **Ls-Files Filter:** We run `git ls-files` to get solely the tracked text files (excluding binary garbage).
2. **ThreadPool Executor:** The script fires off multiple parallel workers to run `git blame --line-porcelain` concurrently across CPUs.
3. **Regex Extraction:** It rips the UNIX timestamps out of the porcelain format and bins them into "years".
When checking out a specific month's commit, the script runs `git blame` on every tracked file.

---
1. **ls-files filter:** Runs `git ls-files` to get only tracked text files.
2. **ThreadPool execution:** Uses multiple workers to run `git blame --line-porcelain` concurrently.
3. **Regex extraction:** Extracts UNIX timestamps from the porcelain output and groups them by year.

## The Fossil Extraction (`add_fossils.py`)
## Fossil extraction (`add_fossils.py`)

Fossils are pointers to specific, historically significant lines of code that serve as fun easter-eggs for the UI. They are evaluated completely independently to prevent slowing down the main incremental snapshot pipeline.
Fossils are pointers to the oldest lines of code. They are calculated independently so they don't slow down the main pipeline.

```mermaid
stateDiagram-v2
Expand All @@ -62,19 +58,17 @@ stateDiagram-v2
extractor --> AppendMetadataJSON
```

### Historical (Genesis) Protocol
Repos imported from SVN/Mercurial can have wildly inaccurate committer timestamps. We resolve this by running `git log --all --pretty=format:%H %at` to sort all commits explicitly by `author-time`, stepping through the absolute oldest `genesis_depth` commits, and extracting the first line of code ever pushed to the repo's history regardless of branch logic.

### Living (Survivor) Protocol
This focuses strictly on the default branch `HEAD`. It recursively blames the latest state of the codebase. Because it's checking `HEAD`, this value frequently moves as old code is finally refactored out.
### Historical (Genesis) protocol
Repositories imported from SVN or Mercurial often have inaccurate committer timestamps. We resolve this by running `git log --all --pretty=format:%H %at` to sort commits by `author-time`, stepping through the oldest `genesis_depth` commits, and extracting the first line of code pushed to the history.

---
### Living (Survivor) protocol
This runs strictly on the default branch `HEAD`. It recursively blames the latest state of the codebase to find the oldest line still in use. This value shifts as old code gets refactored out.

## Data Delivery via Vanilla UI (`app.js`)
## Data delivery via UI (`app.js`)

The UI is intentionally lightweight. We avoided heavy React or bundle-chain systems to ensure the repository remains simple and easy to fork.
The frontend uses Vanilla JavaScript without a build system to keep the repository simple.

The UI loads `theseus.config.json` via the browser Fetch API, builds out a repository selection grid dynamically, and upon clicking a card, pulls the corresponding static `data/{repo}_data.json`.
The UI fetches `theseus.config.json` via the browser Fetch API, builds a repository selection grid, and loads the corresponding `data/{repo}_data.json` when a user selects a repository.

```mermaid
sequenceDiagram
Expand Down
55 changes: 25 additions & 30 deletions docs/CONFIGURATION.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# ⚙️ Configuration Guide
# Configuration guide

The Ship of Theseus engine operates centrally off a single file: `theseus.config.json`. By modifying this file, you instruct both the Python backend and the JavaScript frontend on which repositories to scrape and display.
The Python backend and the JavaScript frontend both read from `theseus.config.json` to determine which repositories to analyze and display.

## Base Schema (`theseus.config.json`)
## Base schema (`theseus.config.json`)

```json
{
Expand All @@ -20,39 +20,34 @@ The Ship of Theseus engine operates centrally off a single file: `theseus.config
}
```

### Global Settings
### Global settings

* `dataDir` *(string)*: The relative path to the directory where the engine saves output JSONs. Usually `"./data"`. The frontend uses this to know where to fetch data.
* `dataDir` *(string)*: The relative path where the Python script saves JSON files. The frontend uses this to fetch data. Default is `"./data"`.

### Repositories Array
### Repositories array

The `repositories` array takes objects consisting of the following key attributes:
The `repositories` array takes objects with the following keys:

| Key | Type | Description | Example |
| :--- | :---: | :--- | :--- |
| `name` | *String* | A safe, unique identifier. Used as the repo slug (`--repo NAME`) and as the data filenames — `data/raw/{name}_data.json` (raw with blame metadata) and `data/processed/{name}_graph.json` (graph for frontend). Must be kebab-case. | `"django"` |
| `repo` | *String* | The GitHub repository namespace. The engine resolves this to `https://github.com/owner/repo.git`. | `"django/django"` |
| `description` | *String* | A short UI subheading clarifying what the project is. | `"The web framework for perfectionists with deadlines."` |
| `milestones` | *Array* | An optional list of significant events to display on the timeline. | `[{"date": "2024-01", "title": "Launch"}]` |
| `name` | *String* | A unique identifier used for the repo slug (`--repo NAME`) and filenames. Must be kebab-case. | `"django"` |
| `repo` | *String* | The GitHub repository namespace (resolves to `https://github.com/owner/repo.git`). | `"django/django"` |
| `description` | *String* | A short subheading clarifying the project's purpose. | `"The web framework for perfectionists with deadlines."` |
| `milestones` | *Array* | An optional list of events to display on the timeline. | `[{"date": "2024-01", "title": "Launch"}]` |

---
## Milestone structure

## Milestone Structure

The `milestones` array contains objects with the following properties:
Objects in the `milestones` array use these properties:

| Key | Type | Description | Example |
| :--- | :---: | :--- | :--- |
| `date` | *String* | The date of the milestone in `YYYY-MM` format. | `"2024-06"` |
| `title` | *String* | A short, catchy name for the event shown in tooltips. | `"Monorepo Migration"` |
| `description` | *String* | A concise explanation of the event. | `"Unified all integrations into a single repository."` |


---
| `date` | *String* | The date in `YYYY-MM` format. | `"2024-06"` |
| `title` | *String* | The event name shown in tooltips. | `"Monorepo Migration"` |
| `description` | *String* | A short explanation of the event. | `"Unified all integrations into a single repository."` |

## Adding a New Repository
## Adding a new repository

Paste this template into the `repositories` array in `theseus.config.json`:
Add this block to the `repositories` array in `theseus.config.json`:

```json
{
Expand All @@ -69,20 +64,20 @@ Paste this template into the `repositories` array in `theseus.config.json`:
}
```

Then run the pipeline to generate the data:
Run the pipeline to generate the data:

```bash
python -m scripts.run_pipeline --repo REPO-NAME
```

This single command clones the repository, runs quarterly/monthly snapshot analysis, discovers both genesis and survivor fossils, and writes two files:
- `data/raw/{name}_data.json` — master data with per-file blame metadata (pipeline state)
- `data/processed/{name}_graph.json` — cleaned graph data for the frontend (only `snapshot_date` + `composition` per entry)
This command clones the repository, runs the snapshot analysis, extracts the fossils, and writes two files:
- `data/raw/{name}_data.json` — raw data with per-file blame metadata.
- `data/processed/{name}_graph.json` — graph data formatted for the frontend.

The frontend auto-discovers the new data from `data/processed/` — no additional changes needed.
The frontend automatically detects the new data in `data/processed/`.

> [!NOTE]
> Data filenames are derived from `name`: `data/raw/{name}_data.json` and `data/processed/{name}_graph.json`. There is no `file` field to maintain.
> Data filenames are derived directly from the `name` field. You do not need to specify file paths manually in the configuration.

> [!CAUTION]
> Avoid modifying the output data within `data/` manually. Doing so can corrupt the incremental snapshot cache, forcing a full re-clone and re-analysis.
> Do not modify the output data in `data/` manually. Doing so corrupts the incremental snapshot cache, forcing a full re-analysis.
28 changes: 13 additions & 15 deletions docs/DEVOPS.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# 🤖 DevOps & CI/CD Pipeline
# DevOps and CI/CD

The Ship of Theseus engine doesn't just run once—codebases never stop evolving. The system relies entirely on GitHub Actions to provide zero-maintenance "monthly pulses" that autonomously update the data output repository.
The system uses GitHub Actions to run monthly updates on the configured repositories.

## The Automation Engine (`.github/workflows/theseus-engine.yml`)
## The GitHub Actions workflow (`.github/workflows/theseus-engine.yml`)

The primary workflow handles generating the JSON snapshot objects incrementally every month, tracking any changes, and pushing them back to the repository data block.
The primary workflow generates incremental JSON snapshots every month and commits any changes back to the repository.

```mermaid
journey
Expand All @@ -23,20 +23,18 @@ journey
Push JSON to Origin: 5: GitHub Actions
```

### 1. `analyse_repository.py` Trigger
The analyzer looks at `theseus.config.json` and pulls from the local `data/` cache. Because `analyse_repository.py` is fully incremental, it will read `snapshot_date="2025-02"` in the JSON, look at the wall-clock calendar time (e.g. `2025-05`), and figure out that it needs to specifically checkout the repositories at `2025-03`, `2025-04` and `2025-05` to catch up to the current date. It will execute these checkouts locally within the GitHub Actions runner.
### 1. `analyse_repository.py`
The script reads `theseus.config.json` and checks the local `data/` cache. If the latest snapshot in the JSON is `2025-02` and the current month is `2025-05`, the script checks out the repository at `2025-03`, `2025-04`, and `2025-05` to catch up.

### 2. `add_fossils.py --update-survivor` Trigger
Genesis fossils rarely change unless a codebase undergoes an extreme edge-case rewrite of its absolute first commit history. The UI primarily benefits from tracking the *"Living Fossil"*.
### 2. `add_fossils.py --update-survivor`
Genesis fossils rarely change because they point to the very first commit. The UI primarily tracks the "Living Fossil," which moves when old code is deleted.

To save processing time during CI constraints, the Action only triggers `add_fossils.py` with the `--update-survivor` flag, bypassing sorting all commits for Genesis creation completely, and simply updating the `view_commit` tip to track code changes.
To save processing time during CI, the Action only runs `add_fossils.py` with the `--update-survivor` flag, updating the `view_commit` tip to track code changes without re-evaluating the entire history.

### 3. File Re-commit Handling
Finally, the action checks if the snapshot array or the survivor fossil commit length actually triggered a diff against the origin.
### 3. Committing updates
After the scripts run, the Action checks if the output files in `data/` have changed.

If `git status` shows modifications to the JSON payloads inside `data/`, the robotic GitHub Actions bot commits the payload and forces a synchronized write onto `main`. This allows the repository to essentially act as its own self-healing backend Database.

---
If there are modifications, the GitHub Actions bot commits the new JSON payloads to `main`.

> [!TIP]
> Ensure the Action is allowed Write permissions in the repository settings: `Settings -> Actions -> General -> Workflow permissions -> Read and write permissions`. Otherwise, the robotic commit will result in `HTTP 403` and the pipeline will fail silently.
> Ensure the Action has Write permissions in the repository settings: `Settings -> Actions -> General -> Workflow permissions -> Read and write permissions`. Otherwise, the commit attempt will return an `HTTP 403` error and fail silently.
Loading
Loading