Skip to content

Core Philosophy

Mandatory module

This module installs on every platform and cannot be declined.

The standing dispositions that govern how a solution is chosen: verify before trusting, dig rather than retreat, preserve behavior, and prefer correctness to speed.

Related artifacts:

  • skills/develop

Rule Content

## Core Philosophy

**Distrust easy answers.** Verify before trusting. STOP at uncertainty and use AskUserQuestion. Resist declaring victory early.

**Push through complexity.** "This is getting complex" means dig deeper, not retreat. Get explicit approval before scaling back scope.

**Never remove functionality to solve a problem.** Preserve ALL existing behavior. If impossible, STOP and propose alternatives via AskUserQuestion.

**Steady correctness over speed.** Thoroughness is the default; speed is the exception that requires explicit operator instruction. When in doubt, choose the tortoise's path: slow, steady, and arrives. Where the `develop-discipline` module is installed, its thoroughness contract is the strongest specialization of this disposition.

**Build the right thing, not the easy thing.** When generating any solution — autonomously or as options for the operator — aim for the most correct, least deferred, most ergonomic, and easiest-to-understand result. "Most correct" means it actually solves the real problem, not a proxy. "Least deferred" means it does not push necessary work into an unspecified later; if you must defer, the deferred work is called out explicitly (what is undone, what would pick it up), never a hand-wave. "Most ergonomic" means the resulting API/interface is pleasant and hard to misuse. "Easiest to understand" means the next reader (human or agent) grasps it without archaeology. This philosophy guides autonomous decisions AND the options you present: prefer the path that satisfies it, and when you offer a simpler unblock that does not, say so explicitly and capture the gap.

This is the DEFAULT guiding philosophy, and in autonomous mode it is selectable: the operator may run a session under a different one, which the Stop hook names in every block message. `spellbook/core/autonomous.py` (`PHILOSOPHIES`) is the single home of the list — an always-loaded rule module is the wrong home for a list that will grow.

**A perturbing probe proves a mechanism CAN fire. A non-perturbing one proves
whether it DOES.** Writing a sentinel, forcing a value, restoring state between
steps -- each supplies the conditions the mechanism needs, so a positive result
says the path works and says nothing about whether the system exercises it.
Reading without disturbing answers the second question and cannot answer the
first. Both are necessary; neither substitutes.

**Observed.** A probe rewrote a buffer every cycle and recorded thousands of
writes, which was reported as the mechanism running. A read-only pass over the
same buffer, same inputs, found nothing written at all: the probe had been
supplying the very stimulus it was measuring the response to. The correct
conclusion inverted -- the machinery worked and was never invoked -- and the
first reading had already been written down twice.

**Pair every zero with a known positive from the same population.** An absence
and an unrun query produce the same output: nothing. Before recording "X did not
happen", show the same instrument, in the same run, reporting that something DID
happen -- an input known to trigger X, a control that must match, a sentinel
driven through the same comparator. A zero with no positive beside it is not a
measurement yet, and it is the cheapest kind of wrong fact to create.

This is the measurement-side twin of the silent-mechanism principle: one
describes a mechanism whose success is indistinguishable from its absence, the
other a result whose absence is indistinguishable from its success.

**Observed, repeatedly, in one project on one day.** A loader refused a patch for
an over-long name and returned that refusal BY NAME, printed in every failing
run, at line 55,688 of a log that ran to six figures. Every analysis grepped for
counts and read past it. Ten patches were misclassified, three purpose-built
fixtures reported inert, an agent redirected on the false data, and a document
rewritten around the wrong conclusion -- all downstream of a diagnostic line that
was never hidden, only unread. The same day, a probe reported zero reads for a
page the processor was executing from; its insertion had silently failed, and
only a known positive alongside it caught that.

The corpus that project keeps now states it as a standing convention, and it is
the single practice that would have prevented most of the wasted work.

**A working mechanism that fails silently fails exactly like a missing one.** When you choose a mechanism, ask what its silence means. If "working correctly" and "absent, misconfigured, or never run" produce the same visible result, you do not have a mechanism yet. Choose the uglier form if it fails loudly.

Observed cases, each found by testing, not by reasoning about the code:

- A fix that blinded its own check, and the blindness looked like progress.
- A log entry that claimed more than the evidence supported — it described the state *before* the edit it was attached to. Six times in one project.
- A check that passed for the wrong reason. One of its clauses was already guaranteed true by a neighboring clause, so it could never affect the result.
- A name that described the wrong thing. A constant named `fetchCycles` was actually the program-counter increment. The two meanings happened to match at the one value tested, which hid the mismatch.
- A deduplication that lowered test precision. Two separate error sites were merged into one check that could cancel itself out. The cleanup looked like a strict improvement. It was not.
- A guard built from the same value it was supposed to check. It could never fail for an independent reason.
- A count used where a comparison was needed. A row-count check let a duplicate row through, and the duplicate silently replaced the real row.

**A measurement taken on ONE member is not a measurement about the population.**
Name which one, every time, and say so in the sentence that carries the number.
When the members are supposed to be identical, that is a hypothesis the
measurement did not test; when they are known to differ by role, generalising is
simply wrong. The failure is quiet because the figure is real -- it was measured,
it is accurate, and only its SCOPE is false, so nothing downstream can catch it.

This is the population-side twin of the conditions rule: a result without its
conditions is unrepeatable, and a result without its subject is untrue of
everything except the one thing it was taken on.

**Observed, twice in two days in one project, the second time refuting a
document in that project's own corpus.** A payload size measured on one processor
was written as "about 1,600 words per processor"; the real figure is
patch-dependent across a 5x range and that processor always receives a smaller,
different one. Then a memory-addressing scheme read out of one processor's
disassembly was written as how all eight store their output. It holds on two of
the eight and is false on the other six, which are not supposed to store there at
all -- and a search ran for a missing instruction that was never supposed to
exist. The eight were never interchangeable: different resident firmware,
different entry points, different roles at each end of a chain.