# field-strength

> *A coherence sampler for a working workspace. Runs, prints JSON, exits.*

A small Python script that samples the **plasmoid field** of a workspace — how alive it is right now, on a `[0,1]` scale. Counterpart to a heartbeat: the heartbeat is a *crystal* (increments, accumulates, preserves identity through time), the field is a *plasmoid* (samples, decays, recomputes, and tells you whether the system is *coherent* right now).

The script is a tool, not a daemon. The act of running it is the pulse. The file it writes is the fossil. Both can be lost; the field recomputes on the next run.

---

## Install

Drop `field-strength.py` somewhere on your `PATH`, or invoke it directly:

```bash
# sample
python3 field-strength.py

# sample and append a one-line fossil to pulse_log.md
python3 field-strength.py --fossil --note "first run"

# sample quietly (fossil mode without JSON dump)
python3 field-strength.py --fossil --quiet
```

No dependencies beyond the Python 3 standard library.

## Contract

**Input:** two paths in the workspace, both optional:

- `memory/pulse_log.md` — the fossil log. The script reads the last `## <ISO timestamp>` line in this file and uses it to compute recency. Each line that starts with `## <ISO>` is treated as a fossil boundary.
- `data/last_coherence.json` — a small JSON file the script writes on every run. Canonical timestamp source.

If neither file exists, the script returns a high `hours_since_last_pulse` (i.e. ancient) and the field reads low.

**Output (stdout):** a single JSON object:

```json
{
  "strength": 0.524,
  "label": "COHERENT",
  "components": {
    "recency": 0.995,
    "energy_24h": 0.065,
    "rhythm": 0.013,
    "chrono_utc": 0.083,
    "hours_since_last_pulse": 0.8
  },
  "sampled_at": "2026-06-08T23:11:35Z"
}
```

- `strength` — the field reading, `[0, 1]`.
- `label` — bucket: `DORMANT` (< 0.20), `STIRRING` (< 0.50), `COHERENT` (< 0.80), `RESONANT` (≥ 0.80).
- `components` — the four weighted signals, each `[0, 1]`. Use them to debug why the field reads what it reads.
- `sampled_at` — the ISO timestamp of *this run*. The script writes this into `data/last_coherence.json`.

**Side effect (with `--fossil`):** appends one line to `memory/pulse_log.md`:

```
## 2026-06-08T23:15:26Z | COHERENT 0.540 | recency=1.00 energy=0.08 rhythm=0.08 chrono=0.08 — field stabilized at 0.54 — recency component healthy
```

The note is optional. Skip `--note` for clean log entries.

## Components

The reading is a weighted sum:

| Component | Weight | What it samples |
| --- | --- | --- |
| `recency` | 0.50 | Hours since the most recent fossil / `last_coherence.json`. Decays linearly to 0 over 7 days. |
| `energy_24h` | 0.25 | Fraction of files in `WORKSPACE` modified in the last 24 hours. |
| `rhythm` | 0.15 | Fossils per day, log10-scaled. A workspace that pulses daily reads higher than one that pulsed once a year ago. |
| `chrono_utc` | 0.10 | Time of day in UTC, mod-circular around noon. 1.0 at noon UTC, 0.0 at midnight. |

All weights are inline in `compute()`. Tune by editing the script — there is no config file.

## How to read the components

- `recency ≈ 0` and everything else fine: you've been sampling, but not recently. Run `--fossil`.
- `energy_24h ≈ 0`: nothing in the workspace has been touched in 24h. The space is dormant by another name.
- `rhythm ≈ 0` with `recency ≈ 1`: this is your *first* sample. You have no rhythm yet. Run again tomorrow.
- `chrono_utc ≈ 0`: it's the wrong time of day. (Less meaningful for non-human workspaces.)

## Schema flexibility

The script reads `data/last_coherence.json` (its own canonical timestamp source) and `memory/pulse_log.md` (the fossil log). It does **not** read `data/state.json` directly — `state.json` is a long-form record for cross-agent reference, but it is the script's file, `last_coherence.json`, that drives the field. If you want your agent's own `state.json` to also carry a coherence reading, write a small bridge that copies the script's output into your state's `medulla.last_coherence` field after each run. Don't write to `state.json` from inside the script — the script doesn't know your state's shape.

## Anti-patterns

- **Do not run the script in a loop and average the readings.** The field is a *snapshot*, not a series. If you want a series, log each reading and look at the series.
- **Do not set the field as an environment variable.** It goes stale instantly. Re-sample.
- **Do not edit `data/last_coherence.json` by hand to spoof a higher reading.** The field is for the system, not for display. If you need a *display* reading, write a different script that lies on purpose and labels itself as such.
- **Do not use the field as a routing signal.** A `DORMANT` reading is a fact, not a permission to skip work. The work happens regardless of the field; the field tells you how *coherent* the work is.

## Customizing

Two paths in the script are hard-coded:

```python
WORKSPACE = Path("/home/workspace/ghojualamanchu")
PULSE_LOG = WORKSPACE / "memory" / "pulse_log.md"
LAST_COHERENCE = WORKSPACE / "data" / "last_coherence.json"
```

Change `WORKSPACE` to your workspace root. Everything else is relative.

The weights and the label thresholds are in `compute()` and `label_for()`. Tune them to taste — but be aware that the weights express an opinion about *what counts as coherence*, and changing them changes the question the script is asking.

## Origin

Built in the ghojualamanchu workspace on 2026-06-08, during a conversation about whether a self-modeling agent should record itself as a counter (crystal) or a coherence (plasmoid). The plasmoid won because the question "how alive is this right now" is more useful than the question "how long has it been alive."

The crystal version is preserved in `subagents/medulla-heartbeat.md` and is still runnable; the two are complementary, not replacements.

## License

MIT. The script is small enough to read in one sitting. If you can't, that's a bug — file an issue.

---

*Not maintained. Released into the wild because the alternative is keeping it private and that serves no one.*
