Skip to content

World

Example data

The examples use this data and the names todo, today and tasks.

import datetime as dt
from enum import StrEnum
from string.templatelib import Template

from scriptt.world import Fact, Goal, Key, Log, Ref, Row, World


class TodoList(Fact):
    name: Key[str]


class Status(StrEnum):
    OPEN = "open"
    DONE = "done"


class Task(Fact):
    id: Key[str]
    list: Ref[TodoList]
    title: str
    status: Status = Status.OPEN
    due: dt.date | None = None

    def __describe__(self) -> Template:  # how say() names a task
        return t"task {self.id}"


class Note(Fact):
    task: Key[Ref[Task]]
    text: str


todo = World.new(TodoList, Task, Note)
for name in ["home", "work"]:
    todo.add(
        TodoList(name=name),
        source="me",
        true_from="2 Nov 2026",
        known="2 Nov 2026 09:00",
    )
for key, home, title, due, true_from, known in [
    ("t1", "home", "Buy milk", dt.date(2026, 11, 3), "2 Nov 2026", "2 Nov 2026 09:00"),
    (
        "t2",
        "work",
        "Send the report",
        dt.date(2026, 11, 4),
        "2 Nov 2026",
        "2 Nov 2026 09:00",
    ),
    (
        "t3",
        "work",
        "Book the room",
        dt.date(2026, 11, 6),
        "2 Nov 2026",
        "2 Nov 2026 09:00",
    ),
    ("t4", "home", "Call the plumber", None, "2 Nov 2026", "2 Nov 2026 09:00"),
    (
        "t5",
        "home",
        "Water the plants",
        dt.date(2026, 11, 4),
        "2 Nov 2026",
        "2 Nov 2026 09:00",
    ),
    (
        "t6",
        "home",
        "Pay the rent",
        dt.date(2026, 11, 5),
        "3 Nov 2026",
        "3 Nov 2026 12:00",
    ),
]:
    todo.add(
        Task(id=key, list=home, title=title, due=due),
        source="me",
        true_from=true_from,
        known=known,
    )
for task, text in [("t1", "Oat milk"), ("t2", "Use the new template")]:
    todo.add(
        Note(task=task, text=text),
        source="me",
        true_from="2 Nov 2026",
        known="2 Nov 2026 09:00",
    )
milk = todo.on("2 Nov 2026").one(Task, id="t1")
todo.add(
    milk.but(status=Status.DONE),
    source="me",
    true_from="3 Nov 2026",
    known="3 Nov 2026 18:30",
)

today = todo.on("4 Nov 2026")
tasks = today.facts(Task)

World dataclass

World(
    _store: Store = Store(),
    _types: dict[str, type[Fact]] = dict(),
    on_date: date = today(),
    known_time: datetime | None = None,
    until_date: date | None = None,
    _supposed: tuple[Entry, ...] = (),
    _cache: dict = dict(),
)

A log of facts and a view of it on 1 date with what you knew at 1 time. Every view of 1 log shares that log.

Make a world with World.new or World.open. Do not call the constructor. Writing calls such as add and end and retract add to the log. Every other call gives a new view or an answer. It does not change the log.

A view has 3 fields. You can read them. These calls make views with other values.

  • on
  • known
  • at
  • during
  • steps

Attributes:

Name Type Description
on_date date

The day of the view. The default is today. In a during window this is the first day.

known_time datetime | None

The latest time that the view knows. The default None means every entry of the log.

until_date date | None

The end of a during window. This day is not in the window. The default None means the window is the single day on_date.

Example
from scriptt.world import World

todo = World.new(TodoList, Task)
todo.add(
    Task(id="t9", list="home", title="Buy tea"),
    source="me",
    true_from="2 Nov 2026",
)
todo.on("2 Nov 2026").one(Task, id="t9").title  # -> 'Buy tea'

on_date class-attribute instance-attribute

on_date: date = field(default_factory=dt.date.today)

known_time class-attribute instance-attribute

known_time: datetime | None = None

until_date class-attribute instance-attribute

until_date: date | None = None

Make a world

A log of facts and a view of it on 1 date with what you knew at 1 time. Every view of 1 log shares that log.

Make a world with World.new or World.open. Do not call the constructor. Writing calls such as add and end and retract add to the log. Every other call gives a new view or an answer. It does not change the log.

A view has 3 fields. You can read them. These calls make views with other values.

  • on
  • known
  • at
  • during
  • steps

Attributes:

Name Type Description
on_date date

The day of the view. The default is today. In a during window this is the first day.

known_time datetime | None

The latest time that the view knows. The default None means every entry of the log.

until_date date | None

The end of a during window. This day is not in the window. The default None means the window is the single day on_date.

Example
from scriptt.world import World

todo = World.new(TodoList, Task)
todo.add(
    Task(id="t9", list="home", title="Buy tea"),
    source="me",
    true_from="2 Nov 2026",
)
todo.on("2 Nov 2026").one(Task, id="t9").title  # -> 'Buy tea'

size property

size: int

The number of entries in the log.

Each of these calls adds 1 entry.

  • add
  • end
  • retract
  • same
  • map
  • place
  • rename

A supposed change is not an entry.

Example
todo.size  # -> 11

open classmethod

open(
    path: str | Path,
    *types: type[Fact],
    on: Moment | None = None,
) -> World

Open a world that is kept in a DuckDB file. An existing file keeps its log. So you read it again.

Parameters:

Name Type Description Default
path str | Path

The path of the DuckDB file. A new file is made if it does not exist.

required
*types type[Fact]

The types of fact that the file contains. export and later - earlier read the types that you name here or that you add later.

()
on Moment | None

The day of the first view. The default None means today.

None

Returns:

Type Description
World

A world. Its size is the number of entries already in the file.

Example
import pathlib
import tempfile
from scriptt.world import World

path = pathlib.Path(tempfile.mkdtemp()) / "tasks.duckdb"
kept = World.open(path, Task)
kept.add(Task(id="t9", list="home", title="Buy tea"), source="me")
kept.size  # -> 1

new classmethod

new(*types: type[Fact], on: Moment | None = None) -> World

Make a world that is kept in memory. The log is lost when the program ends. Use World.open to keep it.

Parameters:

Name Type Description Default
*types type[Fact]

The types of fact that the world contains. export and later - earlier read the types that you name here or that you add later.

()
on Moment | None

The day of the first view. The default None means today.

None

Returns:

Type Description
World

A new empty world. The built-in types Mapping and Under are always

World

included.

Example
from scriptt.world import World

fresh = World.new(TodoList, Task, on="2 Nov 2026")
fresh.size  # -> 0
fresh.on_date  # -> datetime.date(2026, 11, 2)

Write to the log

A log of facts and a view of it on 1 date with what you knew at 1 time. Every view of 1 log shares that log.

Make a world with World.new or World.open. Do not call the constructor. Writing calls such as add and end and retract add to the log. Every other call gives a new view or an answer. It does not change the log.

A view has 3 fields. You can read them. These calls make views with other values.

  • on
  • known
  • at
  • during
  • steps

Attributes:

Name Type Description
on_date date

The day of the view. The default is today. In a during window this is the first day.

known_time datetime | None

The latest time that the view knows. The default None means every entry of the log.

until_date date | None

The end of a during window. This day is not in the window. The default None means the window is the single day on_date.

Example
from scriptt.world import World

todo = World.new(TodoList, Task)
todo.add(
    Task(id="t9", list="home", title="Buy tea"),
    source="me",
    true_from="2 Nov 2026",
)
todo.on("2 Nov 2026").one(Task, id="t9").title  # -> 'Buy tea'

add

add(
    fact: Fact,
    *,
    source: str,
    true_from: Moment | None = None,
    true_until: Moment | None = None,
    true_on: Moment | None = None,
    known: Moment | None = None,
    once: bool = False,
) -> Self

Add a fact to the log. Give where it came from and when it is true and known.

Use true_from and true_until for a period. Use true_on for 1 day. A fact with none of them is true for all time. A new add with the same key is a new version of the fact. Forward[...] fields may not move back.

Parameters:

Name Type Description Default
fact Fact

The fact to add.

required
source str

Text that says where the fact came from. It is not optional.

required
true_from Moment | None

The first day on which the fact is true. The default None means the fact has always been true.

None
true_until Moment | None

The day on which the fact stops being true. This day is not included. The default None means no end.

None
true_on Moment | None

One day on which the fact is true. It sets true_from to that day and true_until to the next day. The default is None.

None
known Moment | None

The time at which you got the fact. The default None means now.

None
once bool

Whether to do nothing if this source already gave this version with the same dates. The default is False. Use True so that you can read a file again without adding its facts twice.

False

Returns:

Type Description
Self

The same world. So you can chain calls.

Raises:

Type Description
ValueError

A date or time in the text form that when refuses. An example is "8/10/2026".

ValueError

A Forward[...] field of the fact is earlier in the enum than in the last version.

Example
listed = World.new(Task)
listed.add(
    Task(id="t9", list="home", title="Buy tea"),
    source="me",
    true_from="2 Nov 2026",
    known="2 Nov 2026 09:00",
)
listed.add(
    Task(id="t9", list="home", title="Buy tea"),
    source="me",
    true_from="2 Nov 2026",
    known="2 Nov 2026 09:00",
    once=True,
)
listed.size  # -> 1
listed.on("2 Nov 2026").get(Task, id="t9").title  # -> 'Buy tea'

end

end(
    fact: Fact | type[Fact],
    /,
    *,
    on: Moment,
    source: str,
    known: Moment | None = None,
    **key: Any,
) -> Self

Say that a fact stops being true on a day. Views before that day still see it.

An end cuts the versions that start before it. An add from the end day on starts a new period.

Parameters:

Name Type Description Default
fact Fact | type[Fact]

A fact or the type of the fact. If you give a type, also give the key fields as keyword arguments.

required
on Moment

The day on which the fact stops being true. This day is not included.

required
source str

Text that says where this information came from.

required
known Moment | None

The time at which you got this information. The default None means now.

None
**key Any

The key fields of the fact when fact is a type.

{}

Returns:

Type Description
Self

The same world.

Raises:

Type Description
ValueError

A date or time in the text form that when refuses. An example is "8/10/2026".

KeyError

fact is a type and you did not give all of its key fields.

Example
listed = World.new(Task)
listed.add(
    Task(id="t9", list="home", title="Buy tea"),
    source="me",
    true_from="2 Nov 2026",
)
listed.end(Task, id="t9", on="4 Nov 2026", source="me: not needed")
listed.on("3 Nov 2026").get(Task, id="t9").title  # -> 'Buy tea'
listed.on("4 Nov 2026").get(Task, id="t9")  # -> None

retract

retract(
    fact: Fact | type[Fact],
    /,
    *,
    source: str,
    known: Moment | None = None,
    **key: Any,
) -> Self

Say that a fact was never true. Views that you knew before the retract still see it.

The log keeps the fact and adds a retract entry.

Parameters:

Name Type Description Default
fact Fact | type[Fact]

A fact or the type of the fact. If you give a type, also give the key fields as keyword arguments.

required
source str

Text that says where this information came from.

required
known Moment | None

The time at which you got this information. The default None means now.

None
**key Any

The key fields of the fact when fact is a type.

{}

Returns:

Type Description
Self

The same world.

Raises:

Type Description
KeyError

fact is a type and you did not give all of its key fields.

Example
listed = World.new(Task)
listed.add(
    Task(id="t9", list="home", title="Buy tea"),
    source="me",
    known="2 Nov 2026 20:00",
)
listed.retract(
    Task,
    id="t9",
    source="me: typed by mistake",
    known="2 Nov 2026 21:00",
)
listed.on("3 Nov 2026").get(Task, id="t9")  # -> None
listed.known("2 Nov 2026 20:30").on("3 Nov 2026").get(
    Task, id="t9"
).title  # -> 'Buy tea'
listed.size  # -> 2

same

same(
    a: Fact,
    b: Fact,
    *,
    source: str,
    known: Moment | None = None,
) -> Self

Say that 2 facts are 1 thing.

The thing keeps the identity of the fact that joined first. Its fields merge. The newest value that is not None wins. A later same that repeats or reverses a join changes nothing. Use conflicts to see fields that disagree.

Parameters:

Name Type Description Default
a Fact

The first fact.

required
b Fact

The second fact. It must have the same type as a.

required
source str

Text that says where this information came from.

required
known Moment | None

The time at which you got this information. The default None means now.

None

Returns:

Type Description
Self

The same world.

Raises:

Type Description
TypeError

a and b are not facts of 1 type.

Example
lists = World.new(Task)
a = Task(id="t9", list="home", title="Buy tea")
b = Task(id="t10", list="home", title="Get tea")
lists.add(a, source="me", true_from="2 Nov 2026").add(
    b, source="me", true_from="2 Nov 2026"
)
lists.same(a, b, source="me: one task")
lists.on("2 Nov 2026").facts(Task).count()  # -> 1

map

map(
    a: Any,
    b: Any,
    match: Match,
    *,
    source: str,
    true_from: Moment | None = None,
    known: Moment | None = None,
) -> Self

Say that a term in 1 word list maps to a term in another.

This adds a Mapping fact. Read the mappings of a term with mapped.

Parameters:

Name Type Description Default
a Any

The term in the first word list. An enum member uses its value.

required
b Any

The term in the other word list.

required
match Match

The kind of the mapping.

required
source str

Text that says where this information came from.

required
true_from Moment | None

The first day on which the mapping is true. The default None means always.

None
known Moment | None

The time at which you got this information. The default None means now.

None

Returns:

Type Description
Self

The same world.

Example
from scriptt.world import Match, World

terms = World.new()
terms.map("shopping", "errands", Match.NARROWER, source="me")
terms.mapped("shopping").count()  # -> 1

place

place(
    child: Any,
    *,
    under: Any,
    source: str,
    rel: Rel = PART_OF,
    true_from: Moment | None = None,
    true_until: Moment | None = None,
    known: Moment | None = None,
) -> Self

Say that a thing is directly under a parent. The thing is a part of the parent (the default) or a kind of the parent (rel=Rel.IS_A).

This adds an Under fact. A thing has 1 parent for each kind of link. A later place of the same child and kind replaces the parent. Read the tree with these calls.

  • parents
  • children
  • ancestors
  • descendants

Parameters:

Name Type Description Default
child Any

The thing. An enum member uses its value.

required
under Any

The parent.

required
rel Rel

The kind of link. The default is Rel.PART_OF.

PART_OF
source str

Text that says where this information came from.

required
true_from Moment | None

The first day on which the place is true. The default None means always.

None
true_until Moment | None

The day on which it stops. This day is not included. The default None means no end.

None
known Moment | None

The time at which you got this information. The default None means now.

None

Returns:

Type Description
Self

The same world.

Raises:

Type Description
ValueError

A date or time in the text form that when refuses. An example is "8/10/2026".

Example
tree = World.new()
tree.place("Buy milk", under="Groceries", source="me")
tree.parents("Buy milk").first()  # -> 'Groceries'

rename

rename(
    of: type[Fact],
    *,
    old: str,
    new: str,
    source: str,
    known: Moment | None = None,
) -> Self

Say that a field of a type now has a new name. Earlier entries are read with the new name.

Declare the new field on the type first.

Parameters:

Name Type Description Default
of type[Fact]

The type with the new field already declared.

required
old str

The old name of the field.

required
new str

The new name of the field.

required
source str

Text that says where this information came from.

required
known Moment | None

The time at which you got this information. The default None means now.

None

Returns:

Type Description
Self

The same world.

Raises:

Type Description
KeyError

The type has no field with the new name.

Example
class Chore(Fact, name="chore"):
    id: Key[str]
    hours: float


class ChoreNow(Fact, name="chore"):
    id: Key[str]
    minutes: float


chores = World.new(Chore)
chores.add(Chore(id="t9", hours=1.5), source="me")
chores.rename(ChoreNow, old="hours", new="minutes", source="me: new name")
chores.get(ChoreNow, id="t9").minutes  # -> 1.5

Views

A log of facts and a view of it on 1 date with what you knew at 1 time. Every view of 1 log shares that log.

Make a world with World.new or World.open. Do not call the constructor. Writing calls such as add and end and retract add to the log. Every other call gives a new view or an answer. It does not change the log.

A view has 3 fields. You can read them. These calls make views with other values.

  • on
  • known
  • at
  • during
  • steps

Attributes:

Name Type Description
on_date date

The day of the view. The default is today. In a during window this is the first day.

known_time datetime | None

The latest time that the view knows. The default None means every entry of the log.

until_date date | None

The end of a during window. This day is not in the window. The default None means the window is the single day on_date.

Example
from scriptt.world import World

todo = World.new(TodoList, Task)
todo.add(
    Task(id="t9", list="home", title="Buy tea"),
    source="me",
    true_from="2 Nov 2026",
)
todo.on("2 Nov 2026").one(Task, id="t9").title  # -> 'Buy tea'

on

on(date: Moment) -> World

Give a view of the world on a day.

The view keeps the facts that are true on that day. It does not change what the view knows.

Parameters:

Name Type Description Default
date Moment

The day. A time of day is ignored. A time zone does not move the day.

required

Returns:

Type Description
World

A new view of the same log.

Raises:

Type Description
ValueError

A date or time in the text form that when refuses. An example is "8/10/2026".

Example
todo.on("2 Nov 2026").facts(Task).count()  # -> 5
todo.on("4 Nov 2026").facts(Task).count()  # -> 6

known

known(time: Moment) -> World

Give a view that knows only what you knew by a time.

Parameters:

Name Type Description Default
time Moment

The time. A date with no time of day means the end of that day.

required

Returns:

Type Description
World

A new view of the same log. Its known_time is the time.

Raises:

Type Description
ValueError

A date or time in the text form that when refuses. An example is "8/10/2026".

Example
view = todo.on("4 Nov 2026").known("2 Nov 2026 09:00")
view.facts(Task).count()  # -> 5

at

at(date: Moment) -> World

Give the world as you saw it on a day. It has the facts true on that day and known by the end of it.

Parameters:

Name Type Description Default
date Moment

The day.

required

Returns:

Type Description
World

A new view of the same log.

Raises:

Type Description
ValueError

A date or time in the text form that when refuses. An example is "8/10/2026".

Example
todo.at("4 Nov 2026").known_time
# -> datetime.datetime(2026, 11, 4, 23, 59, 59, 999999)

during

during(start: Moment, until: Moment) -> World

Give a window of days. A fact is in the view if it was true on any day of the window.

The window starts on start and ends before until. A fact with 2 versions in the window is seen once. It shows its latest version. history gives every version.

Parameters:

Name Type Description Default
start Moment

The first day of the window.

required
until Moment

The day after the last day of the window. This day is not in the window.

required

Returns:

Type Description
World

A new view of the same log.

Raises:

Type Description
ValueError

A date or time in the text form that when refuses. An example is "8/10/2026".

Example
listed = World.new(Task)
listed.add(
    Task(id="t9", list="home", title="Buy tea"),
    source="me",
    true_from="2 Nov 2026",
)
listed.end(Task, id="t9", on="4 Nov 2026", source="me: not needed")
listed.add(
    Task(id="t9", list="home", title="Buy more tea"),
    source="me: again",
    true_from="10 Nov 2026",
)
listed.during("3 Nov 2026", "11 Nov 2026").get(Task, id="t9").title
# -> 'Buy more tea'

steps

steps(
    by: Step, count: int, *, known_too: bool = False
) -> Facts[World]

Give a series of views from this view's day. There is one view for each step.

Parameters:

Name Type Description Default
by Step

The distance between the views.

required
count int

The number of views. The first view is on this view's day.

required
known_too bool

Whether each view knows only what you knew by the end of its day. The default False means each view keeps the known_time of this view.

False

Returns:

Type Description
Facts[World]

A collection of count views.

Raises:

Type Description
ValueError

A date or time in the text form that when refuses. An example is "8/10/2026".

Example
from scriptt.world import Step

views = todo.on("2 Nov 2026").steps(Step.DAY, 3)
[view.facts(Task).count() for view in views]  # -> [5, 6, 6]

suppose

suppose(
    *,
    add: Iterable[Fact] = (),
    true_from: Moment | None = None,
    end: Iterable[Fact] = (),
    retract: Iterable[Fact] = (),
) -> World

Give a view with changes that the log does not keep.

A supposed fact wins over every logged fact. A supposed end uses the day of this view. Ask what would differ with supposed - today.

Parameters:

Name Type Description Default
add Iterable[Fact]

Facts to add. The default is none.

()
true_from Moment | None

The first day on which the added facts are true. The default None means always.

None
end Iterable[Fact]

Facts to end on this view's day. The default is none.

()
retract Iterable[Fact]

Facts that were never true. The default is none.

()

Returns:

Type Description
World

A new view. The log does not change.

Example
report = today.one(Task, id="t2")
done = today.suppose(add=[report.but(status=Status.DONE)])
done.one(Task, id="t2").status.value  # -> 'done'
today.one(Task, id="t2").status.value  # -> 'open'

Read facts

A log of facts and a view of it on 1 date with what you knew at 1 time. Every view of 1 log shares that log.

Make a world with World.new or World.open. Do not call the constructor. Writing calls such as add and end and retract add to the log. Every other call gives a new view or an answer. It does not change the log.

A view has 3 fields. You can read them. These calls make views with other values.

  • on
  • known
  • at
  • during
  • steps

Attributes:

Name Type Description
on_date date

The day of the view. The default is today. In a during window this is the first day.

known_time datetime | None

The latest time that the view knows. The default None means every entry of the log.

until_date date | None

The end of a during window. This day is not in the window. The default None means the window is the single day on_date.

Example
from scriptt.world import World

todo = World.new(TodoList, Task)
todo.add(
    Task(id="t9", list="home", title="Buy tea"),
    source="me",
    true_from="2 Nov 2026",
)
todo.on("2 Nov 2026").one(Task, id="t9").title  # -> 'Buy tea'

facts

facts(of: type[T]) -> Facts[T]

Give every fact of 1 type that is true in this view.

Parameters:

Name Type Description Default
of type[T]

The type of fact.

required

Returns:

Type Description
Facts[T]

A collection of facts with 1 fact for each thing. Versions merge as same

Facts[T]

says. A type that other types subclass

Facts[T]

(class Chore(Task)) also gives the facts of those types.

Example
today.facts(Task).count()  # -> 6
today.facts(Note).count()  # -> 2

one

one(of: type[T], **key: Any) -> T

Give 1 fact by its key fields. The fact must exist in this view.

Parameters:

Name Type Description Default
of type[T]

The type of fact.

required
**key Any

A value for every key field of the type.

{}

Returns:

Type Description
T

The fact.

Raises:

Type Description
LookupError

No fact of this type with this key is true in this view. Use get to get None instead.

KeyError

You did not give all the key fields.

Example
today.one(Task, id="t2").title  # -> 'Send the report'

get

get(of: type[T], **key: Any) -> T | None

Give 1 fact by its key fields. It is None if the fact is not true in this view.

Parameters:

Name Type Description Default
of type[T]

The type of fact.

required
**key Any

A value for every key field of the type.

{}

Returns:

Type Description
T | None

The fact or None.

Raises:

Type Description
KeyError

You did not give all the key fields.

Example
today.get(Task, id="t99")  # -> None

Read the log

A log of facts and a view of it on 1 date with what you knew at 1 time. Every view of 1 log shares that log.

Make a world with World.new or World.open. Do not call the constructor. Writing calls such as add and end and retract add to the log. Every other call gives a new view or an answer. It does not change the log.

A view has 3 fields. You can read them. These calls make views with other values.

  • on
  • known
  • at
  • during
  • steps

Attributes:

Name Type Description
on_date date

The day of the view. The default is today. In a during window this is the first day.

known_time datetime | None

The latest time that the view knows. The default None means every entry of the log.

until_date date | None

The end of a during window. This day is not in the window. The default None means the window is the single day on_date.

Example
from scriptt.world import World

todo = World.new(TodoList, Task)
todo.add(
    Task(id="t9", list="home", title="Buy tea"),
    source="me",
    true_from="2 Nov 2026",
)
todo.on("2 Nov 2026").one(Task, id="t9").title  # -> 'Buy tea'

history

history(fact: Fact) -> Facts[Entry]

Give every entry of the log for 1 fact with all 3 times. The oldest entry comes first.

The entries are those that the view knows. Supposed changes are at the end.

Parameters:

Name Type Description Default
fact Fact

A fact. Its type and key choose the entries.

required

Returns:

Type Description
Facts[Entry]

A collection of Entry objects.

Example
listed = World.new(Task)
listed.add(Task(id="t9", list="home", title="Buy tea"), source="me")
listed.add(Task(id="t9", list="home", title="Buy tea x2"), source="me: x2")
probe = Task(id="t9", list="", title="")
[entry.source for entry in listed.history(probe)]
# -> ['me', 'me: x2']

origin

origin(
    fact: T, field: Callable[[T], V]
) -> Origin[T, V]

Say where 1 value of a fact came from. Give the value and the entry of the log that wrote it.

For a fact of this view, the entry is the one that wrote the version that this view shows. After same it is the entry of the member that gave the value. Give a changed copy to find when another value was written. An example is view.origin(fact.but(p=0.5), lambda f: f.p). Then the entry is the first of the last run of entries that give it.

Parameters:

Name Type Description Default
fact T

The fact.

required
field Callable[[T], V]

A function that reads exactly 1 field of the fact. An example is lambda p: p.amount.

required

Returns:

Type Description
Origin[T, V]

An Origin.

Raises:

Type Description
ValueError

The function does not read exactly 1 field of the fact.

LookupError

No entry of the log gives the fact this value.

Example
report = today.one(Task, id="t2")
today.origin(report, lambda t: t.title).entry.known
# -> datetime.datetime(2026, 11, 2, 9, 0)

References, kinds and problems

A log of facts and a view of it on 1 date with what you knew at 1 time. Every view of 1 log shares that log.

Make a world with World.new or World.open. Do not call the constructor. Writing calls such as add and end and retract add to the log. Every other call gives a new view or an answer. It does not change the log.

A view has 3 fields. You can read them. These calls make views with other values.

  • on
  • known
  • at
  • during
  • steps

Attributes:

Name Type Description
on_date date

The day of the view. The default is today. In a during window this is the first day.

known_time datetime | None

The latest time that the view knows. The default None means every entry of the log.

until_date date | None

The end of a during window. This day is not in the window. The default None means the window is the single day on_date.

Example
from scriptt.world import World

todo = World.new(TodoList, Task)
todo.add(
    Task(id="t9", list="home", title="Buy tea"),
    source="me",
    true_from="2 Nov 2026",
)
todo.on("2 Nov 2026").one(Task, id="t9").title  # -> 'Buy tea'

problems

problems(*goals: Goal) -> Facts[Problem]

Give every problem in this view. A problem is one of these.

  • A Ref field that points to no fact.
  • A thing that place puts under itself.
  • A fact that a goal fails.

A Ref problem is found for every type in the world with no setup. A goal adds its own rule. A goal with every=True gives 1 problem for each fact that fails it. A count or sum goal that is not met gives 1 problem.

Parameters:

Name Type Description Default
*goals Goal

Rules to check as well. A rule such as "at most 1 employer at a time" is a goal with every=True.

()

Returns:

Type Description
Facts[Problem]

A collection of problems. The Ref problems come first. An empty collection

Facts[Problem]

means no problem.

Example
from scriptt.world import Fact, Key, Ref, World


class TodoList(Fact):
    name: Key[str]


class Task(Fact):
    id: Key[str]
    list: Ref[TodoList]
    title: str


lists = World.new(TodoList, Task)
lists.add(TodoList(name="home"), source="me")
lists.add(Task(id="t1", list="home", title="Buy milk"), source="me").add(
    Task(id="t2", list="work", title="Send the report"), source="me"
)
[p.message for p in lists.problems()]
# -> ["task t2: list 'work' is not the key of any TodoList."]

about

about(fact: Fact, hops: int = 1) -> Facts[Fact]

Give the fact and every fact that points to it with a Ref field. This gives "everything about this thing".

Parameters:

Name Type Description Default
fact Fact

The fact. An example is a list.

required
hops int

How many references to follow back. 2 also gives the facts that point to those facts (the notes of the tasks of a list). The default is 1.

1

Returns:

Type Description
Facts[Fact]

A collection with the fact first. Then come the facts that point to it. They

Facts[Fact]

come type by type. A Ref to a type

Facts[Fact]

it subclasses counts too.

Example
from scriptt.world import Fact, Key, Ref, World


class TodoList(Fact):
    name: Key[str]


class Task(Fact):
    id: Key[str]
    list: Ref[TodoList]
    title: str


lists = World.new(TodoList, Task)
lists.add(TodoList(name="home"), source="me")
lists.add(Task(id="t1", list="home", title="Buy milk"), source="me").add(
    Task(id="t2", list="work", title="Send the report"), source="me"
)
lists.about(lists.one(TodoList, name="home")).count()
# -> 2: the list and its task

parents

parents(thing: Any, rel: Rel = PART_OF) -> Facts[str]

Give the direct parent of a thing.

Parameters:

Name Type Description Default
thing Any

The thing. An enum member uses its value.

required
rel Rel

The kind of link to follow. The default is Rel.PART_OF.

PART_OF

Returns:

Type Description
Facts[str]

A collection of 0 or 1 text value.

Example
tree = World.new()
tree.place("Buy milk", under="Groceries", source="me")
list(tree.parents("Buy milk"))  # -> ['Groceries']

children

children(thing: Any, rel: Rel = PART_OF) -> Facts[str]

Give the things that are directly under a thing.

Parameters:

Name Type Description Default
thing Any

The parent. An enum member uses its value.

required
rel Rel

The kind of link to follow. The default is Rel.PART_OF.

PART_OF

Returns:

Type Description
Facts[str]

A collection of text values.

Example
tree = World.new()
tree.place("Buy milk", under="Groceries", source="me")
list(tree.children("Groceries"))  # -> ['Buy milk']

ancestors

ancestors(thing: Any, rel: Rel = PART_OF) -> Facts[str]

Give the parent of a thing and the parent of that parent and so on. The nearest comes first.

Parameters:

Name Type Description Default
thing Any

The thing. An enum member uses its value.

required
rel Rel

The kind of link to follow. The default is Rel.PART_OF.

PART_OF

Returns:

Type Description
Facts[str]

A collection of text values. It stops if the tree has a loop.

Example
tree = World.new()
tree.place("Buy milk", under="Groceries", source="me")
tree.place("Groceries", under="Shopping", source="me")
list(tree.ancestors("Buy milk"))
# -> ['Groceries', 'Shopping']

descendants

descendants(thing: Any, rel: Rel = PART_OF) -> Facts[str]

Give every thing under a thing at any depth. The nearest come first.

Parameters:

Name Type Description Default
thing Any

The parent. An enum member uses its value.

required
rel Rel

The kind of link to follow. The default is Rel.PART_OF.

PART_OF

Returns:

Type Description
Facts[str]

A collection of text values.

Example
tree = World.new()
tree.place("Buy milk", under="Groceries", source="me")
tree.place("Groceries", under="Shopping", source="me")
list(tree.descendants("Shopping"))
# -> ['Groceries', 'Buy milk']

conflicts

conflicts(of: type[Fact]) -> Facts[Conflict]

Give the fields on which the members of 1 thing disagree after same.

Parameters:

Name Type Description Default
of type[Fact]

The type of fact.

required

Returns:

Type Description
Facts[Conflict]

A collection of Conflict objects, 1 for each field that has 2 values or

Facts[Conflict]

more.

Example
lists = World.new(Task)
a = Task(id="t9", list="home", title="Buy tea")
b = Task(id="t10", list="home", title="Get tea")
lists.add(a, source="me").add(b, source="me").same(
    a, b, source="me: one task"
)
lists.conflicts(Task)[0].values  # -> ('Buy tea', 'Get tea')

mapped

mapped(
    term: Any, match: Match | None = None
) -> Facts[Mapping]

Give the mappings from a term in either direction.

Parameters:

Name Type Description Default
term Any

The term. An enum member uses its value.

required
match Match | None

The kind of mapping to keep. The default None means every kind.

None

Returns:

Type Description
Facts[Mapping]

A collection of Mapping facts.

Example
from scriptt.world import Match, World

terms = World.new()
terms.map("shopping", "errands", Match.NARROWER, source="me")
terms.mapped("errands", Match.NARROWER)[0].a  # -> 'shopping'

Compare views

A log of facts and a view of it on 1 date with what you knew at 1 time. Every view of 1 log shares that log.

Make a world with World.new or World.open. Do not call the constructor. Writing calls such as add and end and retract add to the log. Every other call gives a new view or an answer. It does not change the log.

A view has 3 fields. You can read them. These calls make views with other values.

  • on
  • known
  • at
  • during
  • steps

Attributes:

Name Type Description
on_date date

The day of the view. The default is today. In a during window this is the first day.

known_time datetime | None

The latest time that the view knows. The default None means every entry of the log.

until_date date | None

The end of a during window. This day is not in the window. The default None means the window is the single day on_date.

Example
from scriptt.world import World

todo = World.new(TodoList, Task)
todo.add(
    Task(id="t9", list="home", title="Buy tea"),
    source="me",
    true_from="2 Nov 2026",
)
todo.on("2 Nov 2026").one(Task, id="t9").title  # -> 'Buy tea'

__sub__

__sub__(earlier: World) -> Changes[Fact]

Compare 2 views as later - earlier. Give the facts that the later view adds or removes or changes.

Parameters:

Name Type Description Default
earlier World

The view to compare with. It must share the log or at least use the same types.

required

Returns:

Type Description
Changes[Fact]

A Changes object.

Example
earlier = todo.on("4 Nov 2026").known("2 Nov 2026 09:00")
changes = today - earlier
changes.added.count(), changes.removed.count()  # -> (1, 0)

Goals

A log of facts and a view of it on 1 date with what you knew at 1 time. Every view of 1 log shares that log.

Make a world with World.new or World.open. Do not call the constructor. Writing calls such as add and end and retract add to the log. Every other call gives a new view or an answer. It does not change the log.

A view has 3 fields. You can read them. These calls make views with other values.

  • on
  • known
  • at
  • during
  • steps

Attributes:

Name Type Description
on_date date

The day of the view. The default is today. In a during window this is the first day.

known_time datetime | None

The latest time that the view knows. The default None means every entry of the log.

until_date date | None

The end of a during window. This day is not in the window. The default None means the window is the single day on_date.

Example
from scriptt.world import World

todo = World.new(TodoList, Task)
todo.add(
    Task(id="t9", list="home", title="Buy tea"),
    source="me",
    true_from="2 Nov 2026",
)
todo.on("2 Nov 2026").one(Task, id="t9").title  # -> 'Buy tea'

check

check(goal: Goal[T]) -> GoalResult[T]

Test a goal in this view.

Parameters:

Name Type Description Default
goal Goal[T]

The goal. The second parameter of a rule with 2 parameters is this view.

required

Returns:

Type Description
GoalResult[T]

A GoalResult. It says whether the goal is met. It also lists the facts

GoalResult[T]

that fail the rule.

Example
has_note = Goal(
    "every task has a note",
    Task,
    lambda t, view: view.get(Note, task=t.id) is not None,
    every=True,
)
result = today.check(has_note)
result.met, sorted(t.id for t in result.failing)
# -> (False, ['t3', 't4', 't5', 't6'])

SQL and export

A log of facts and a view of it on 1 date with what you knew at 1 time. Every view of 1 log shares that log.

Make a world with World.new or World.open. Do not call the constructor. Writing calls such as add and end and retract add to the log. Every other call gives a new view or an answer. It does not change the log.

A view has 3 fields. You can read them. These calls make views with other values.

  • on
  • known
  • at
  • during
  • steps

Attributes:

Name Type Description
on_date date

The day of the view. The default is today. In a during window this is the first day.

known_time datetime | None

The latest time that the view knows. The default None means every entry of the log.

until_date date | None

The end of a during window. This day is not in the window. The default None means the window is the single day on_date.

Example
from scriptt.world import World

todo = World.new(TodoList, Task)
todo.add(
    Task(id="t9", list="home", title="Buy tea"),
    source="me",
    true_from="2 Nov 2026",
)
todo.on("2 Nov 2026").one(Task, id="t9").title  # -> 'Buy tea'

sql

sql(query: Template) -> Facts[Row]

Run SQL for a question that the verbs do not cover. Give the query as a t-string.

{Task} names the typed table of a fact type in this view. {Log} names the log. Every other {value} is sent as a parameter and is never pasted into the query. Only 1 SELECT or PIVOT or UNPIVOT or EXPLAIN runs. So the log cannot change. DuckDB can still read local files (read_csv and read_text). Write {Log} and do not write log. The plain table contains every entry. This includes entries that the view does not know yet.

Parameters:

Name Type Description Default
query Template

A t-string. A value in it takes no format and no conversion.

required

Returns:

Type Description
Facts[Row]

A collection of rows with 1 for each row of the result. The column names are

Facts[Row]

the field names.

Raises:

Type Description
TypeError

query is not a t-string.

ValueError

A value has a format or a conversion or the text is not 1 SELECT or PIVOT or UNPIVOT or EXPLAIN.

Example
limit = dt.date(2026, 11, 4)
rows = today.sql(
    t"FROM {Task} SELECT title, due WHERE due > {limit} ORDER BY due DESC"
)
rows[0].title  # -> 'Book the room'

relation

relation(of: type[Fact]) -> DuckDBPyRelation

Give the facts of 1 type in this view as a DuckDB relation with typed columns. An enum field is a DuckDB ENUM.

Each call makes its own table. So a relation keeps its rows when you make another view.

Parameters:

Name Type Description Default
of type[Fact]

The type of fact.

required

Returns:

Type Description
DuckDBPyRelation

A duckdb.DuckDBPyRelation.

Example
len(today.relation(Task))  # -> 6

frame

frame(of: type[Fact]) -> DataFrame

Give the facts of 1 type as a Polars DataFrame. It has 1 row for each fact. This needs the extra scriptt[frames].

Parameters:

Name Type Description Default
of type[Fact]

The type of fact.

required

Raises:

Type Description
ModuleNotFoundError

Polars is not installed.

Example
today.frame(Note).shape  # -> (2, 2)

export

export(folder: str | Path) -> Facts[Path]

Write 1 Parquet file for each type that has facts in this view.

Each file is named after the type. An example is task.parquet.

Parameters:

Name Type Description Default
folder str | Path

The folder for the files. It is made if it does not exist.

required

Returns:

Type Description
Facts[Path]

A collection of the paths of the files.

Example
import pathlib
import tempfile

files = today.export(pathlib.Path(tempfile.mkdtemp()))
sorted(path.name for path in files)
# -> ['note.parquet', 'task.parquet', 'todolist.parquet']