Commit Graph

2 Commits

Author SHA1 Message Date
bigmerge c48db6c2a8 ANSWER: is 3 a position, or a convention we agreed on?
Both, at different layers, and the split is the same as everywhere else. The
NUMERAL is convention -- int_to_str was already form 1, because no position
determines that twelve is written 1 then 2 in base ten. The NUMBER is a
position: three things are three things regardless of notation.

But the sharper answer follows from `love = 0`. A bare `3` is a MAGNITUDE WITH
NO AXIS. It is not a position until something gives it a direction, which is
exactly why 3.days needs a calendar and why time_add(t, n, "min") had to carry
its axis as a string.

PREDICTIONS AND RESULTS
  P1 numeral = convention, number = position                TRUE
  P2 a bare literal is dimensionless until context types it TRUE
  P3 there is a measurable place where El guesses           TRUE
  P4 Instant + Int is not caught though Duration + Int is   TRUE
  P5 the rule catches it                                    TRUE
  P6 nothing legitimate in the tree relies on it            TRUE

P3/P4 IS THE DEFECT, and it was found by reasoning from the philosophy and then
measured. Duration + Int was refused -- "an Int carries no unit" -- while

    let t: Instant = now()
    let u: Instant = t + 3

compiled to raw (t + 3) and reported CLEAN. Adding a dimensionless number to a
point is worse than adding it to a displacement: it silently moves the instant
by an unspecified amount. 3 of what? Whatever the representation happens to be,
which is the leak itself. The asymmetry had no justification; the rule was
simply never written.

P6 MATTERED. Two calendar tests looked like Instant + Int:

    let later: Instant = i + 1.hour
    let later: Instant = base + 15.hours

They are not. `1.hour` lexes to a Duration -- el_duration_from_nanos(1LL *
3600000000000LL) -- and both stay clean. That is the whole answer demonstrated
in one line: t + 3 is refused because 3 has no axis; t + 1.hour is accepted
because .hour supplies one.

104/104 native + 2 new, integration green, fixpoint ok.
2026-08-17 09:49:50 -05:00
bigmerge e8e25a07b4 EXPERIMENT: temporal adjudication moves out; the placeholder stays
The previous pass moved the type DATA and left the judgment inline, which I
stated rather than hid. This finishes it.

PREDICTIONS AND RESULTS
  P1 codegen can emit operand-type relations               TRUE
                                                           "main calls temporal:instant_plus_instant"
  P2 the affine rules are a small closed set as data       TRUE  6 rules
  P3 violations still caught at build time                 TRUE  exit=1
  P4 the reporter leaves codegen                           TRUE  4538 -> 4507
  P5 the TIME_TYPE_ERROR placeholder must STAY             TRUE

P5 is the boundary of this whole approach. The emitter has to emit SOMETHING
for an illegal expression -- it cannot emit nothing and it cannot decide what
the program meant. So the placeholder is irreducible in the same way the AST
traversal was: what moved is the judgment and the wording, not the fact that
something must be written.

The rules are affine algebra and the set is closed because there are only two
kinds of thing. An Instant is a POINT, a Duration is a DISPLACEMENT: add a
displacement to a point, subtract two points for a displacement, combine
displacements. Nothing else is meaningful, which is why the enumeration in
temporal.rel cannot grow the way an allowlist does.

A defect in my own checker, found by running it: the .rel file uses aligned
columns and my awk assumed a single space, so the message came out with the
rule key still prefixed. Same class as the multi-line header parse in the arity
pass -- formatting assumptions that only fail when you look at the output.

98/98 native, 6/6 temporal_query.sh, fixpoint ok.
2026-08-17 09:27:42 -05:00