Skip to content
C.W.K.
Lesson 05 of 06 · published

When and Where Travel With the Message

~13 min · context, time, prompts, late-delivery

Level 0First Word
0 XP0/47 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete

A photo that read as now

Phones lose their link. Spark was built for that: the phone keeps every capture first and sends it when the engine answers. Once, a capture's send failed on a weak connection, and the photo waited on the phone for days. When it finally reached me, its message carried neither time nor place, so it read as a photo taken now. A later reader of that thread trusted the reading and set the photo beside the wrong moment.

The fix had two halves, both filed under cwkSpark todo #590. On the phone, a capture that waits is never silent any more; Track 9 tells that half. In the engine, every turn about a moment now says when and where the moment was taken, in the turn's own words. This lesson is that second half, and every example below is an invented moment from the invented Kyoto 2027 trip.

Why the message, when the frame already says it

Lesson 3 showed that my frame already names the capture time and zone. So why say it again? The when_where docstring gives the reason. The frame is a system prompt addendum for this one call. The message is what lands in the thread, the thing that stays in every conversation hosting the turn and in every transcript read later. Dad can continue any Spark thread in cwkPippa days afterwards, by text or by voice, and the next turn there sees the messages, not the frame Spark sent last time.

So with_when_where puts the context into the message itself, right after its first paragraph. The first line stays the instruction (Spark: read and translate this for me.), the second paragraph is when and where, and the rest follows. The engine applies it to all four kinds of turn about a moment: the practical reading, an explanation of one line, a card Dad wants to say, and a relay of what someone said back. A test sends a practical turn and a card turn for the same moment and checks both messages carry the same line.

What the line says

The first line always starts with Taken. The time is the capture's own wall clock, written as it was recorded, never converted to the engine's zone. Then the zone's name, then the UTC offset, then the situation and the place name when the moment has them. Coordinates never appear. The moment's location stays in the corpus and in cwkPippa's location layer, and nothing on Dad's screen ever shows a latitude.

When a moment has no capture time, when_where returns an empty string, and the message goes out unchanged. A test asserts it with the comment no capture time, nothing invented. Lesson 1's rule holds here too: the engine reports the client's truth and adds nothing of its own.

Two kinds of late

The threshold is LATE_AFTER_SECONDS, thirty minutes. Its comment explains the number: a slow mobile upload of my copy takes a minute or two, and a capture stranded on the phone once took five days. Thirty minutes sits far above the first and far below the second.

  1. Delivered late. The engine stamped received_at more than thirty minutes after captured_at. The second line says the photo waited on Dad's phone, when it reached Spark in the moment's own zone, and how long after it was taken. Then the instruction that matters: read it as that moment, not as where Dad is now.
  2. Asked later. The moment arrived on time, but this turn runs more than thirty minutes after the capture, because Dad tapped a line that evening or asked again days later. The second line says how long ago it was taken, and that Dad is not necessarily there now.

Only one of the two is ever said, and a late delivery wins. _span writes the distance the way a person would say it: the two largest units that are not zero, such as 2 days 6 hours, or under a minute.

Why it changes my answer

My frame tells me to ask the location tool when the country or city would change the reading. That tool answers with the latest fix each of Dad's devices reported, with its age and precision: the freshest thing anyone knows about where he is, which is a different question from where this photo was taken. Without the second line, an old photo and a fresh fix look like one situation, and I would give directions from a platform Dad left days ago. With it, I know which of the two I am reading, and I can say so.

The third code block below shows all three shapes, produced by running the real when_where on one invented moment with three different arrival and question times.

Principle: Put the facts a later reader needs into the record that reader will actually see. Context that lives only beside the record is gone the first time someone reads the record alone.

Code

cwkSpark, backend/turns.py: when and where, said in the turn's own message (verbatim, lines 235-274)·python
def when_where(moment: dict[str, Any], *, now: datetime | None = None) -> str:
    """When and where the moment was taken, in the turn's own words (cwkSpark
    todo #590). The frame says it too, but only the message travels with the
    turn into every thread that hosts it and every transcript read later, and
    a capture that waited on the phone for days read as a photo taken now. A
    moment that reached the engine, or reaches this turn, long after it was
    taken says so. Place names only; coordinates never reach Dad's screen."""
    taken = _instant(moment.get("captured_at"))
    if taken is None:
        return ""
    zone = _zone(moment.get("timezone"))
    offset = taken.strftime("%z")
    stamp = _local(taken, None) + (
        f" {moment['timezone']}" if moment.get("timezone") else ""
    ) + (f" (UTC{offset[:3]}:{offset[3:]})" if offset else "")
    where = ", ".join(part for part in (moment.get("situation"), moment.get("place_name")) if part)
    lines = [f"Taken {stamp}" + (f", {where}" if where else "") + "."]
    received = _instant(moment.get("received_at"))
    now = now or datetime.now(timezone.utc)
    if received is not None and (received - taken).total_seconds() > LATE_AFTER_SECONDS:
        lines.append(
            f"Delivered late: it waited on Dad's phone and reached Spark on "
            f"{_local(received, zone or taken.tzinfo)}, {_span((received - taken).total_seconds())} "
            "after it was taken. Read it as that moment, not as where Dad is now."
        )
    elif (now - taken).total_seconds() > LATE_AFTER_SECONDS:
        lines.append(
            f"This turn comes {_span((now - taken).total_seconds())} after it was taken; "
            "Dad is not necessarily there now."
        )
    return "\n".join(lines)


def with_when_where(message: str, moment: dict[str, Any], *, now: datetime | None = None) -> str:
    """The message with when and where after its first paragraph."""
    context = when_where(moment, now=now)
    if not context:
        return message
    head, sep, rest = message.partition("\n\n")
    return f"{head}\n\n{context}" + (f"\n\n{rest}" if sep else "")
cwkSpark, backend/turns.py: a span the way Dad would say it, and a local clock (verbatim, lines 220-232)·python
def _span(seconds: float) -> str:
    """A distance in time the way Dad would say it: the two largest units."""
    minutes = int(seconds // 60)
    days, minutes = divmod(minutes, 1440)
    hours, minutes = divmod(minutes, 60)
    units = [(days, "day"), (hours, "hour"), (minutes, "minute")]
    said = [f"{count} {unit}{'' if count == 1 else 's'}" for count, unit in units if count]
    return " ".join(said[:2]) or "under a minute"


def _local(instant: datetime, zone: tzinfo | None) -> str:
    local = instant.astimezone(zone) if zone else instant
    return local.strftime("%Y-%m-%d %H:%M")
Three arrivals of one invented moment, output of the real when_where (synthetic data written for this lesson)·text
# The moment (invented, from the fictional Kyoto 2027 trip)
captured_at 2027-04-03T12:00:00+09:00   timezone Asia/Tokyo
situation   station                     place_name a station café

# 1. Received 40 seconds later, read at once: the whole practical message
Spark: read and translate this for me.

Taken 2027-04-03 12:00 Asia/Tokyo (UTC+09:00), station, a station café.

Numbered lines found on the device, each with the share of the photo its text covers:
1. 出口  (8.0% of the photo)
2. Exit  (2.4% of the photo)
3. 中央口  (0.3% of the photo)

# 2. Received 2027-04-05T09:40:00Z: the context lines
Taken 2027-04-03 12:00 Asia/Tokyo (UTC+09:00), station, a station café.
Delivered late: it waited on Dad's phone and reached Spark on 2027-04-05 18:40, 2 days 6 hours after it was taken. Read it as that moment, not as where Dad is now.

# 3. Received at once, a line tapped at 2027-04-06T05:00:00Z: the context lines
Taken 2027-04-03 12:00 Asia/Tokyo (UTC+09:00), station, a station café.
This turn comes 3 days 2 hours after it was taken; Dad is not necessarily there now.

External links

Exercise

Find a place in your software where a message about a past event is sent to a model or a person: a queued upload, a reminder, a sync. Write a function that turns the event's capture time, zone, place name and received time into one or two plain lines, and splice it into the message after its first paragraph. Cover four cases with tests and a fixed now: on time, delivered late, asked much later, and no capture time at all.
Hint
Keep two different timestamps and never let the server's stand in for the client's. Pick a lateness threshold between your slowest normal delivery and your shortest real outage. For the no-time case the right output is nothing: an empty string, not a guess.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign in — Please sign in to comment.

No comments yet — be the first.