Teaching AI about Third Time

bfinn’s LessWrong post describes the Third Time workflow several ways: with examples, with natural language pseudocode procedures, with visual aids, … I think it’s great and I’ve used it to implement various Third Time GUIs.

Now that it’s 2025, it only makes sense for me to figure out how to get AI agents to write the GUIs instead. :technologist: But when I try, I find myself arguing with them a lot.

Third Time is just complex enough that an AI can write a design doc about it, and I can approve that doc, but I can still disapprove of the implementation because we disagree on how to handle some edge case. When faced with ambiguity, I often see AIs make technocentric assumptions that simplify the code but make the software less usable, like:

  • If the user overruns a break, their accrued break time goes negative
  • The user is always working or resting

I’ve been experimenting with conceptual models as a solution. I read about them pre-AI in Daniel Jackson’s book “The Essence of Software” and loved the idea, but couldn’t get my software team on board. My team at work still isn’t on board, but after hours, I find that AI is easier to convince to try something new.

The structure of a conceptual model forces edge cases to the fore in the same way that writing code does, but is general enough to describe software in an implementation-independent way. It supports state changes, but is flexible enough to support timeless aspects of a system, like invariants.

Conceptual models are designed to capture the way users think about the software. For example, the book decomposes Gmail into “Message” and “Thread” and “Label” concepts whose relationships are crisply defined, then explains usability issues as either miscommunication about the underlying concept (do you know if labels apply to Messages or Threads? what about Stars?), or deviations that the implementation make from the underlying concept.

Anyway, long story short, when I collaboratively wrote a conceptual model for Third Time instead of a free-form design doc, AI finally understood what I meant. :person_shrugging:

I thought people here might find my collaboratively-written version of that model useful:


Concept: TimeSegment

A TimeSegment is a fundamental, indivisible, and historical record of a single, continuous activity. It is the atomic building block of a session’s history.

Signature

  • Properties:

    • type: The kind of activity that occurred during this segment (Work or Rest).
    • start: The instant in time when the segment began.
    • end: The instant in time when the segment concluded.
  • Constraints (Invariants):

    • The end instant must occur at or after the start instant.

Concept: TimeLedger

A TimeLedger is a chronologically ordered history of recorded work and rest activities. It is the single source of truth from which all current states are derived.

A ledger is intended to be ephemeral and can be discarded when its accumulated history is no longer relevant, such as at the end of a work day. There is no expectation that accumulated rest counters have any use across long periods of inactivity.

Signature

  • Relations:

    • segments: An ordered list of TimeSegment individuals, sorted chronologically.
  • Derived Properties (as functions of time):

    • availableRest(t): A function that returns the available rest time at a given instant t.
    • overRestTime(t): A function that returns the total accumulated over-rest time at a given instant t.
  • Formal Definition of Derived Properties:
    The values are defined recursively as piecewise functions over the ledger’s segments.

    • Base Case: For any time t before the start of the first segment, availableRest(t) and overRestTime(t) are both 0.
    • Recursive Step: The values for any time t after the start of the first segment are defined based on the segment or gap it falls into.
      • If t falls within a segment s:
        • If s is a Work segment:
          • availableRest(t) = availableRest(s.start) + (t - s.start) / 3
          • overRestTime(t) = overRestTime(s.start) (Over-rest time is not paid down by working.)
        • If s is a Rest segment:
          • availableRest(t) = max(0, availableRest(s.start) - (t - s.start))
          • overRestTime(t) = overRestTime(s.start) + max(0, (t - s.start) - availableRest(s.start))
      • If t falls between two segments or after the last segment: Its value is the same as the value at the end of the preceding segment.
  • Constraints (Invariants):

    • segments in the ledger must not overlap in time.

Concept: Timer (The Controller)

The Timer is an interactive, real-time device. Its state represents a tentative, uncommitted segment that has a start time but whose end time is always the present moment.

A user interface for a Timer will typically show ledger.availableRest(now) for a ledger that includes the Timer’s tentative segment.

  • Signature:
    • state: The current interactive state (Paused, Working, or Resting).
    • activeSegmentStart: A timestamp marking when the current, in-progress segment began.
5 Likes

I normally don’t write code to manage a whole TimeLedger with all its TimeSegments. I usually just store two values: availableRest(timer.activeSegmentStart) and overRestTime(timer.activeSegmentStart). Because usually the history isn’t useful for what I’m trying to do.

The model above is just how I think about the ledger. Having it all spelled out lets AI do the right thing even when the code implements only a special case.

1 Like

I love that approach, it really does formalise it neatly. It sounds like there are three kinds of segments, which take up the whole timeline: work, rest, and other. You’re only really interested in the ration of work to rest, and therefore in knowing when each work and rest segment starts and finishes.

Yeah! A ledger with “other” segments would map onto/implement the TimeLedger concept and thus be compatible with all its related concepts.

Since the ability to map concepts onto one another is so flexible, there’s actually a strong pressure to keep concepts to the absolute minimum to make them maximally reusable, which is why I didn’t specify at all what to do about the time that’s not work or rest.

1 Like

I also find LLMs can be really helpful for fleshing out specs! I have a hard time thinking through all the details and edge cases myself, and LLMs can be great for helping with that.

You might also find this blog post interesting, which goes into detail on a similar approach!

1 Like