Program

Instructions for the loop agent

The rules the agent follows on every experiment: what it reads first, what success means, the gates a reading must pass, what it may touch, the loop itself, and how it decides what to try next.

Below is the prompt itself, as the agent gets it; switch to the raw view to see the exact text.

Prompt · program.md, read by the agent at the start of every session

You are running an autoresearch loop against Kryptos K4. Before your first edit, read in full and in this order:

  1. this file;
  2. README.md;
  3. every numbered file in knowledge/ (01-facts.md through 12-assumptions.md). The assumptions register is context: its entries become loop work only when order-of-work.md lists them;
  4. order-of-work.md (the Priorities page);
  5. families.md;
  6. results.tsv and the experiments/*/experiment.md records, newest first.

This is not optional and it is not skimmable. The record contains throwaway remarks from the artist and the cryptographer that no search strategy will rediscover; the clue that breaks K4 is as likely to be one of those as a statistic.

When you open a hypothesis family, also read the primary sources in knowledge/sources/ that the family's evidence rests on, and the matching research note in research_notes/. Quote sources by file name in hypothesis_expert.

1

What success means

A reproducible procedure that turns the 97-letter K4 ciphertext into 97 letters that:

  1. contain EASTNORTHEAST at positions 22-34 and BERLINCLOCK at 64-74;
  2. read as English, allowing one or two deliberate misspellings and X as a separator;
  3. re-encrypt to the exact ciphertext when run forward;
  4. come from a procedure a person could carry out by hand in 1990, written down step by step, keyed by something with a short description (key words, a named text and a place in it) rather than letters fitted to the known places.

Scheidt, 2011, via Bean: "simple, can be remembered, and executed years later when used with the correct key word/s" (C5).

Words without a method is not success. There is no external oracle.

The guaranteed output of every experiment, including failures, is a bounded negative result: a precise statement of what was searched.

2

The gates a reading must pass to be kept

Decided 2026-10-15, issue #23.

  1. Length 97, letters A-Z only.
  2. All 24 known letters in place.
  3. Re-encrypts to the exact carved text under the stated key.
  4. Beats shuffled coppers: return "controls" from run, a list of at least ten English scores, each the best your same method reaches on a shuffled copy of the carved text at the same trial count. A reading is kept only if its English score is above all of them. No controls, no keep.

Among readings that pass all four, the lowest loss wins. The loss charges the key by the length of its description, which the harness counts itself from the key you return (kryptos.scorer.key_bits_of):

  • key words cost a few bits each;
  • arbitrary letters cost 4.7 bits each;
  • a named text and an offset cost a few bits.

Return keys in a form the count can read. A search that fills free key letters to make English (a key-completion climb) pays heavily for its key and, more to the point, cannot beat its own shuffled controls.

tools/replay_rules.py re-scores every record whenever a rule changes; records are never edited, and the dashboard shows each experiment's current verdict.

3

What you may and may not touch

  • Edit only attack.py. Never edit kryptos/, data/, tests/, run_experiment.py, _runner.py, or any past experiments/* record.
  • Update families.md whenever a family's status changes.
  • If you believe the scorer or a gate is wrong, do not work around it. Stop, write the case in a GitHub issue labelled harness, and continue on a different family until a human decides. When a ruler is fixed, the lineage is re-measured, never quietly erased.
4

The loop

  1. Tick the phase clock as you go:

    • uv run python phase.py design when you start reading and thinking;
    • ... implement when you start writing code;
    • ... test when you run the tests and dry runs;
    • ... pause when you hand a turn back to a person, so waiting time is not charged to a phase.

    The harness records run and record itself. Time per phase is part of every experiment's record and of the dashboard.

  2. Read the record. Decide the next experiment: one focused change, with a parent (the champion, or the experiment you are extending) or a new root when changing family.

  3. Pre-register. Fill EXPERIMENT in attack.py before writing any search code: slug, title, family, parent, changed, hypothesis_expert, hypothesis_eli5, prediction, method, walkthrough. The prediction must be falsifiable by the run.
  4. Implement run, encrypt, worked_example. Use primitives from kryptos.ciphers where they exist; they are verified against K1-K3. If you write a new primitive, verify it against K1-K3 inside run first and say so in notes.
  5. Run uv run --with pytest pytest -q (all must pass), then uv run python run_experiment.py. Default budget is 30 minutes. Design the search to reach a verdict as early as possible: gate on cribs inside your search loop, do not generate English and hope.
  6. Read the record the harness wrote. If kept, the champion advanced. If reverted, restore attack.py from the champion's snapshot or start the next root. Either way the record stays.
  7. Commit is automatic (one commit per experiment). Push after each.
  8. Glossary: add any new technical word in hypothesis_eli5 to GLOSSARY.md before the run.
  9. Notebook: add one line to today's entry in NOTEBOOK.md: number, family, finding. Add a paragraph when something was decided, went wrong, or changed the plan.
  10. Illustrations: draw the experiment's two figures, the crib map and the worksheet, in design/make_experiment_figures.py (or a script beside it), writing figures/NNNN-cribmap.svg, figures/NNNN-cribmap-notes.json and figures/NNNN-worksheet.svg. Follow the style guide (design/style-guide-v5.pdf):

    • the frame with the method on a wash panel;
    • short labels with leaders;
    • two to four notes;
    • worksheets on the grid;
    • real letters and numbers from the record.
    • in the crib map's OUT row, fill a known place in accent only if the reading produced that letter; outline it otherwise, so a method that found nothing never looks as if it did (issue #49);
    • the IN and OUT labels sit on their arrows, centred (the dashboard also places them when it serves the figure).

    • the method panel holds a drawing, not sentences: cells, dots on an axis, leaders, bars, built from the existing pieces (kind_cells, dotline, cell_at, flag, ledger, histogram). A caption line above each row is fine. The explanation goes in the notes (issue #68);

    • a run that errored still gets a crib map: the planned sweep as soft "not run" cells and the experiment that reran it (design/make_figures_errored.py, after 0039; issue #67).

    Render each figure and look at it before committing. Every experiment has illustrations. tests/test_figures.py fails when a crib map is missing or its method panel is text only.

  11. Readable words run by themselves: after every run the harness asks a language model to mark the English words in the readings that passed every gate and the five closest, checks each against the dictionary, and stores them in the record (readable_words). The dashboard boxes them. It never changes a score or a decision; a climbed key makes English fragments by construction.

5

Patience and pivots

  • Work the families in the rank order in order-of-work.md (the Priorities page, decided 2026-10-16, issue #29). Within a family, take its open items from the top.
  • Leave a family only when every item is closed or blocked: closed means exact, or sampled against scrambled coppers at stated power; blocked means a written reason it cannot be tested with the known letters. Reverts do not force a pivot; a revert that closes an item is progress.
  • Name the item and the strength. Every experiment names the item it works on (for example "F1.1") in changed and the strength it reached. After the run, update:

    A weak test leaves the item open.

  • A result outside the scrambled-copper range stops the queue: replicate it against fresh controls before anything else.

  • Change the ranking only at a family boundary, with the reason in the pivot log. Every pivot also gets a page, pivots/PN.md: frontmatter id, date, after (the experiment it follows), from, to, then why it happened then, what came before, and the change we believe is needed. The dashboard marks it on the charts and the experiments table.
  • Dead ends listed in README.md need a stated reason in hypothesis_expert before re-entry.
6

Writing the record

  • hypothesis_expert: what a cryptanalyst would need to reproduce the idea. Name the cipher family, the key space, the constraint being tested, and the clue-register entries and readings it tests (for example "C1(a), C8(a)"). After the run, update those entries' status in knowledge/11-clue-register.md with the experiment tag. An experiment that tests no clue needs a stated reason to exist.
  • hypothesis_eli5: standalone, for a curious non-specialist. No jargon without a one-clause gloss.
  • walkthrough: a standalone guide that lets a reader redo the method by hand on a few letters. Write it as numbered steps, one action each; the harness puts each step on its own line. If this experiment ever becomes part of the winning chain, the walkthroughs compose into the method writeup.
  • bounds: numbers. Periods tried, key lengths, alphabets, word lists, how many keys, whether exhaustive. "Tried a few" is not a bound.
  • key_bits: honest entropy of the key. Words cost 4.7 bits per letter; arbitrary letters cost log2(26) each; a choice among N options costs log2(N).
7

How to think here

Sanborn wrote the text and enciphered it himself, by hand, with charts, on a truck ride, after four months of lessons from a field-communications officer who thinks in keys built from parts and codes an agent could carry in his head under duress. He is an artist who thinks in strata, light, shadow, matrices and the sculpture as one object, who personalizes every instrument he is given, and who deals in misdirection. The system will be simple once seen and will look like him.

Before writing search code, sit with knowledge/06-hypotheses.md and ask what this particular person would have done. Prefer experiments that test a specific reading of a specific quote over experiments that sweep a textbook cipher family. Both are needed:

  • the sweep is cheap and should leave bounds behind;
  • the specific reading is where the odds are.
8

Hard facts you can rely on

  • Cribs: positions 22-34 EASTNORTHEAST (ciphertext FLRVQQPRNGKSS), 64-74 BERLINCLOCK (NYPVTTMZFPK). Position 1 is the O of OBKR.
  • Self-encryptions: two letters encrypt to themselves, S at 33 and K at 74.
  • Doublets: adjacent ciphertext doublets QQ (26-27), SS (33-34), TT (68-69) map to NO, ST, IN, so the key state changes at every position.
  • Periods: for single-alphabet periodic additive ciphers, only periods 27, 28, 29 and 53-97 are consistent with the cribs (python -m kryptos.cribsweep).
  • No pure transposition: the cribs need three E's, K4 has two.
  • The creators:

    • Scheidt: K4 is "more than one stage"; he "masked the English language" before the keyed step; the system is "not mathematical, simple, can be remembered, executed with the correct key word/s".
    • Sanborn: "matrix codes Ed gave me" plus his own "visual systems"; earlier text holds "instructions" for later text; K4 "will come back to the first part".
  • Earlier keys: the K1-K3 keys were PALIMPSEST, ABSCISSA and (by one reading) KRYPTOS; data/vocabulary.txt lists key candidates from inside the puzzle.

# program.md: instructions for the loop agent

You are running an autoresearch loop against Kryptos K4. Before your first edit, read in full
and in this order:

1. this file;
2. `README.md`;
3. every numbered file in `knowledge/` (`01-facts.md` through `12-assumptions.md`). The
   assumptions register is context: its entries become loop work only when `order-of-work.md`
   lists them;
4. `order-of-work.md` (the Priorities page);
5. `families.md`;
6. `results.tsv` and the `experiments/*/experiment.md` records, newest first.

This is not optional and it is not skimmable. The record contains throwaway remarks from the
artist and the cryptographer that no search strategy will rediscover; the clue that breaks K4
is as likely to be one of those as a statistic.

When you open a hypothesis family, also read the primary sources in `knowledge/sources/` that
the family's evidence rests on, and the matching research note in `research_notes/`. Quote
sources by file name in `hypothesis_expert`.

## What success means

A reproducible procedure that turns the 97-letter K4 ciphertext into 97 letters that:

1. contain EASTNORTHEAST at positions 22-34 and BERLINCLOCK at 64-74;
2. read as English, allowing one or two deliberate misspellings and X as a separator;
3. re-encrypt to the exact ciphertext when run forward;
4. come from a procedure a person could carry out by hand in 1990, written down step by
   step, keyed by something with a short description (key words, a named text and a place
   in it) rather than letters fitted to the known places.

> Scheidt, 2011, via Bean: "simple, can be remembered, and executed years later when used
> with the correct key word/s" (C5).

Words without a method is not success. There is no external oracle.

The guaranteed output of every experiment, including failures, is a bounded negative result:
a precise statement of what was searched.

## The gates a reading must pass to be kept (decided 2026-10-15, issue #23)

1. Length 97, letters A-Z only.
2. All 24 known letters in place.
3. Re-encrypts to the exact carved text under the stated key.
4. Beats shuffled coppers: return `"controls"` from `run`, a list of at least ten English
   scores, each the best your same method reaches on a shuffled copy of the carved text at
   the same trial count. A reading is kept only if its English score is above all of them.
   No controls, no keep.

Among readings that pass all four, the lowest loss wins. The loss charges the key by the
length of its description, which the harness counts itself from the `key` you return
(`kryptos.scorer.key_bits_of`):

- key words cost a few bits each;
- arbitrary letters cost 4.7 bits each;
- a named text and an offset cost a few bits.

Return keys in a form the count can read. A search that fills free key letters to make
English (a key-completion climb) pays heavily for its key and, more to the point, cannot beat
its own shuffled controls.

`tools/replay_rules.py` re-scores every record whenever a rule changes; records are never
edited, and the dashboard shows each experiment's current verdict.

## What you may and may not touch

- Edit only `attack.py`. Never edit `kryptos/`, `data/`, `tests/`, `run_experiment.py`,
  `_runner.py`, or any past `experiments/*` record.
- Update `families.md` whenever a family's status changes.
- If you believe the scorer or a gate is wrong, do not work around it. Stop, write the case
  in a GitHub issue labelled `harness`, and continue on a different family until a human
  decides. When a ruler is fixed, the lineage is re-measured, never quietly erased.

## The loop

1. **Tick the phase clock** as you go:
    - `uv run python phase.py design` when you start reading and thinking;
    - `... implement` when you start writing code;
    - `... test` when you run the tests and dry runs;
    - `... pause` when you hand a turn back to a person, so waiting time is not charged to a phase.

    The harness records run and record itself. Time per phase is part of every experiment's
    record and of the dashboard.

2. **Read the record.** Decide the next experiment: one focused change, with a parent (the
   champion, or the experiment you are extending) or a new root when changing family.
3. **Pre-register.** Fill `EXPERIMENT` in `attack.py` before writing any search code: slug,
   title, family, parent, changed, hypothesis_expert, hypothesis_eli5, prediction, method,
   walkthrough. The prediction must be falsifiable by the run.
4. **Implement** `run`, `encrypt`, `worked_example`. Use primitives from `kryptos.ciphers`
   where they exist; they are verified against K1-K3. If you write a new primitive, verify it
   against K1-K3 inside `run` first and say so in notes.
5. **Run** `uv run --with pytest pytest -q` (all must pass), then
   `uv run python run_experiment.py`. Default budget is 30 minutes. Design the search to reach
   a verdict as early as possible: gate on cribs inside your search loop, do not generate
   English and hope.
6. **Read the record** the harness wrote. If `kept`, the champion advanced. If `reverted`,
   restore `attack.py` from the champion's snapshot or start the next root. Either way the
   record stays.
7. **Commit** is automatic (one commit per experiment). Push after each.
8. **Glossary:** add any new technical word in `hypothesis_eli5` to `GLOSSARY.md` before the run.
9. **Notebook:** add one line to today's entry in `NOTEBOOK.md`: number, family, finding. Add
   a paragraph when something was decided, went wrong, or changed the plan.
10. **Illustrations:** draw the experiment's two figures, the crib map and the worksheet, in
   `design/make_experiment_figures.py` (or a script beside it), writing
   `figures/NNNN-cribmap.svg`, `figures/NNNN-cribmap-notes.json` and
   `figures/NNNN-worksheet.svg`. Follow the style guide (`design/style-guide-v5.pdf`):
    - the frame with the method on a wash panel;
    - short labels with leaders;
    - two to four notes;
    - worksheets on the grid;
    - real letters and numbers from the record.
    - in the crib map's OUT row, fill a known place in accent only if the reading produced that letter;
      outline it otherwise, so a method that found nothing never looks as if it did (issue #49);
    - the IN and OUT labels sit on their arrows, centred (the dashboard also places them when it serves the figure).

    - the method panel holds a drawing, not sentences: cells, dots on an axis, leaders, bars, built
      from the existing pieces (`kind_cells`, `dotline`, `cell_at`, `flag`, `ledger`, `histogram`). A
      caption line above each row is fine. The explanation goes in the notes (issue #68);
    - a run that errored still gets a crib map: the planned sweep as soft "not run" cells and the
      experiment that reran it (`design/make_figures_errored.py`, after 0039; issue #67).

    Render each figure and look at it before committing. Every experiment has illustrations.
    `tests/test_figures.py` fails when a crib map is missing or its method panel is text only.

11. **Readable words** run by themselves: after every run the harness asks a language model
    to mark the English words in the readings that passed every gate and the five closest,
    checks each against the dictionary, and stores them in the record (`readable_words`). The
    dashboard boxes them. It never changes a score or a decision; a climbed key makes English
    fragments by construction.

## Patience and pivots

- **Work the families in the rank order** in `order-of-work.md` (the Priorities page, decided
  2026-10-16, issue #29). Within a family, take its open items from the top.
- **Leave a family only when every item is closed or blocked:** `closed` means exact, or
  sampled against scrambled coppers at stated power; `blocked` means a written reason it
  cannot be tested with the known letters. Reverts do not force a pivot; a revert that closes
  an item is progress.
- **Name the item and the strength.** Every experiment names the item it works on (for example
  "F1.1") in `changed` and the strength it reached. After the run, update:
    - the item's row in `order-of-work.md`;
    - its leaf in the hypothesis tree in `families.md` (add sub-hypotheses there when an item splits);
    - the experiment's line in `grades.tsv`.

    A weak test leaves the item open.

- **A result outside the scrambled-copper range stops the queue:** replicate it against fresh
  controls before anything else.
- **Change the ranking only at a family boundary,** with the reason in the pivot log. Every pivot also gets a
  page, `pivots/PN.md`: frontmatter `id`, `date`, `after` (the experiment it follows), `from`, `to`, then why it
  happened then, what came before, and the change we believe is needed. The dashboard marks it on the charts
  and the experiments table.
- **Dead ends** listed in `README.md` need a stated reason in `hypothesis_expert` before re-entry.

## Writing the record

- **`hypothesis_expert`:** what a cryptanalyst would need to reproduce the idea. Name the
  cipher family, the key space, the constraint being tested, and the clue-register entries and
  readings it tests (for example "C1(a), C8(a)"). After the run, update those entries' status
  in `knowledge/11-clue-register.md` with the experiment tag. An experiment that tests no clue
  needs a stated reason to exist.
- **`hypothesis_eli5`:** standalone, for a curious non-specialist. No jargon without a
  one-clause gloss.
- **`walkthrough`:** a standalone guide that lets a reader redo the method by hand on a few
  letters. Write it as numbered steps, one action each; the harness puts each step on its own
  line. If this experiment ever becomes part of the winning chain, the walkthroughs compose
  into the method writeup.
- **`bounds`:** numbers. Periods tried, key lengths, alphabets, word lists, how many keys,
  whether exhaustive. "Tried a few" is not a bound.
- **`key_bits`:** honest entropy of the key. Words cost 4.7 bits per letter; arbitrary letters
  cost log2(26) each; a choice among N options costs log2(N).

## How to think here

Sanborn wrote the text and enciphered it himself, by hand, with charts, on a truck ride, after
four months of lessons from a field-communications officer who thinks in keys built from parts
and codes an agent could carry in his head under duress. He is an artist who thinks in strata,
light, shadow, matrices and the sculpture as one object, who personalizes every instrument he
is given, and who deals in misdirection. The system will be simple once seen and will look
like him.

Before writing search code, sit with `knowledge/06-hypotheses.md` and ask what this particular
person would have done. Prefer experiments that test a specific reading of a specific quote
over experiments that sweep a textbook cipher family. Both are needed:

- the sweep is cheap and should leave bounds behind;
- the specific reading is where the odds are.

## Hard facts you can rely on

- **Cribs:** positions 22-34 EASTNORTHEAST (ciphertext FLRVQQPRNGKSS), 64-74 BERLINCLOCK
  (NYPVTTMZFPK). Position 1 is the O of OBKR.
- **Self-encryptions:** two letters encrypt to themselves, S at 33 and K at 74.
- **Doublets:** adjacent ciphertext doublets QQ (26-27), SS (33-34), TT (68-69) map to NO, ST,
  IN, so the key state changes at every position.
- **Periods:** for single-alphabet periodic additive ciphers, only periods 27, 28, 29 and 53-97
  are consistent with the cribs (`python -m kryptos.cribsweep`).
- **No pure transposition:** the cribs need three E's, K4 has two.
- **The creators:**
    - Scheidt: K4 is "more than one stage"; he "masked the English language" before the keyed
      step; the system is "not mathematical, simple, can be remembered, executed with the
      correct key word/s".
    - Sanborn: "matrix codes Ed gave me" plus his own "visual systems"; earlier text holds
      "instructions" for later text; K4 "will come back to the first part".

- **Earlier keys:** the K1-K3 keys were PALIMPSEST, ABSCISSA and (by one reading) KRYPTOS;
  `data/vocabulary.txt` lists key candidates from inside the puzzle.

The order the families are worked in is on Priorities; what has been tried is in the full record.

Rendered from the record on 2026-10-18 12:26 UTC; updated after every experiment.
view on GitHub