# Understanding the Role of `data/status-log.tsv` as an Append-Only Ledger for Status Transitions in Career-Ops

> Discover how data/status-log.tsv acts as an immutable ledger in santifer/career-ops, tracking every job application status transition with timestamps for a reliable history.

- Repository: [Santiago Fernández de Valderrama/career-ops](https://github.com/santifer/career-ops)
- Tags: internals
- Published: 2026-08-20

---

**`data/status-log.tsv` is the immutable append-only ledger that records every job-application status change with precise timestamps, serving as the temporal source of truth while [`data/applications.md`](https://github.com/santifer/career-ops/blob/main/data/applications.md) tracks only current states.**

In the `santifer/career-ops` open-source job-application tracking system, understanding how status history is preserved is critical for accurate pipeline analytics. The ledger pattern separates **current state** from **state history**, enabling reliable velocity metrics and audit trails that manual tracking cannot provide.

## What Makes `data/status-log.tsv` an Append-Only Ledger

The file enforces append-only semantics: rows are never modified in place. Each entry represents a single state transition with six tab-separated fields:

```

{tracker#}\t{date}\t{from}\t{to}\t{source}\t{note}

```

The `from` and `to` fields use `-` as a sentinel when the prior or target state is unknown. This structure is formally defined in [`DATA_CONTRACT.md`](https://github.com/santifer/career-ops/blob/main/DATA_CONTRACT.md) and enforced by tooling.

### Immutability Guarantees Audit Integrity

Corrections do not overwrite history. Instead, they append new rows with `source: correction`. This preserves the original record while acknowledging the fix—essential for debugging data quality issues without destroying evidence.

## How the Ledger Integrates with the Career-Ops System

### Recording Transitions: `set-status.mjs`

The `set-status.mjs` CLI script is the canonical interface for ledger updates. It synchronizes both the tracker and the log:

```bash

# Mark report #42 as Applied on the actual submission date

node set-status.mjs 42 Applied --on 2024-09-12

```

This command:
1. Updates [`data/applications.md`](https://github.com/santifer/career-ops/blob/main/data/applications.md) with the new status
2. Appends to `data/status-log.tsv`:

```

42	2024-09-12	-	Applied	set-status	Submitted via LinkedIn

```

The `source` field is automatically populated as `set-status` when using the CLI.

### Computing Velocity Metrics: `funnel-velocity.mjs`

The `funnel-velocity.mjs` script reads the ledger to calculate realistic stage-to-stage timing, comparing your pipeline speed against market benchmarks:

```bash
node funnel-velocity.mjs --summary

```

Sample output:

```

Applied→Responded median: 5.2 days
Applied→Interview  median: 12.1 days

```

These metrics depend on the ledger's exact transition dates—not the date you ran the command, which may differ significantly.

### Building Company Evidence: `company-history.mjs`

The `company-history.mjs` script merges the ledger with tracker data, follow-ups, and scan history to generate per-company evidence cards. The temporal precision of the ledger makes these reports defensible during interview discussions.

## Reading and Writing the Append-Only Ledger Programmatically

### Minimal Node.js Parser

```js
import { readFileSync } from 'fs';
import { join } from 'path';

const logPath = join(__dirname, 'data', 'status-log.tsv');
const rows = readFileSync(logPath, 'utf-8')
  .trim()
  .split('\n')
  .map(line => {
    const [tracker, date, from, to, source, note] = line.split('\t');
    return { tracker: Number(tracker), date, from, to, source, note };
  });

console.log(rows.slice(0, 3)); // show first three entries

```

### Adding Retroactive Corrections

For entries recorded with wrong dates, append a correction rather than editing:

```bash
node set-status.mjs 42 Interview --on 2024-09-20 --source correction --note "Corrected applied date"

```

This creates a new ledger row while the original remains intact.

## Data Governance and Source Validation

The `source` column values are restricted by `VALID_SOURCES` in `funnel-velocity.mjs`. Unrecognized sources are treated as "unknown" and excluded from funnel calculations. This prevents corrupted data from skewing velocity metrics.

The [`modes/tracker.md`](https://github.com/santifer/career-ops/blob/main/modes/tracker.md) documentation explicitly warns against manual ledger editing, directing users to `node set-status.mjs` to maintain data integrity.

## Why the Append-Only Ledger Pattern Matters

- **Accurate "days-in-stage" metrics**: Uses actual transition dates, not command execution dates
- **Defensible velocity reports**: `node funnel-velocity.mjs` produces benchmark-comparable numbers
- **Historical integrity**: Retroactive fixes preserve original records for audit
- **Debugging capability**: Full lineage of status changes available for data quality investigation

## Summary

- **`data/status-log.tsv`** is the immutable append-only ledger for status transitions in Career-Ops
- **Six-column TSV format** tracks: tracker number, date, from-status, to-status, source, and optional note
- **`set-status.mjs`** is the required CLI for writing—manual edits violate the data contract
- **`funnel-velocity.mjs`** consumes the ledger for realistic pipeline speed calculations
- **Corrections append only**, ensuring audit trails without destroying history

## Frequently Asked Questions

### What happens if I edit `status-log.tsv` manually?

Manual edits break the append-only invariant and violate [`DATA_CONTRACT.md`](https://github.com/santifer/career-ops/blob/main/DATA_CONTRACT.md). The [`modes/tracker.md`](https://github.com/santifer/career-ops/blob/main/modes/tracker.md) documentation instructs users to always use `node set-status.mjs`. Manual changes may cause `funnel-velocity.mjs` to exclude entries with unrecognized sources or produce incorrect metrics.

### How does the ledger handle initial status entries?

When a new application has no prior state, the `from` field contains `-` as a sentinel value. This distinguishes true initial entries from transitions where the prior state was genuinely unknown due to data quality issues.

### Can I bulk-import historical data into the ledger?

Yes, by formatting entries as valid TSV rows and using `--source correction` or a custom source defined in `funnel-velocity.mjs`'s `VALID_SOURCES`. However, the canonical pattern is to script calls to `set-status.mjs` with appropriate `--on` dates to preserve validation logic.

### How does the ledger differ from [`applications.md`](https://github.com/santifer/career-ops/blob/main/applications.md)?

[`applications.md`](https://github.com/santifer/career-ops/blob/main/applications.md) holds the **current state** only—useful for quick human scanning. `status-log.tsv` holds the **temporal history**—required for analytics. They are complementary: the tracker shows where you stand; the ledger shows how you got there and how long each stage took.