Progress

What's Built

Lotus Agenda 2.0b still runs. A script drives it, reads its screen back, and answers the questions the 1990 design paper declined to answer. So every rule below is a measurement rather than a guess.

Each entry cites the moment it landed, the section of the Users Guide it comes from, and the numbered run of the emulator harness that measured it.

Tier 1 — The engine

Built

Items, notes, the category hierarchy, views, persistence, and a keyboard-first interface in two flavors.

Notes you can actually write — on an item, or on a category

F5 now opens a full-screen note editor in both versions of the app, on an item or on a category — from a section heading or from the category manager. Running the original showed how it behaves: ENTER starts a new line, F5 saves, and ESC asks before throwing away a change, with "No" (keep it) as the answer it suggests. Emptying a note removes it.

The original's copy, cut, paste and mark keys are not built, and its Delete key did something we could not work out, so ours simply deletes forward. The search results view's heading takes a note in the original but not here.

2026-10-03 14:53 -0600 · Users Guide § 2.59 · oracle run 50

Bring a file in and get one out

ALT-E saves the whole database as a Lotus Agenda structured file, and ALT-I brings one in. An import ADDS to what you have, which is what the original's File, Transfer, Import was measured doing, rather than replacing it. The writer is the reader run backwards: a file it writes reads back into the same database, and writing that again produces the same file.

A structured file cannot carry views, the trash, a category's conditions and actions, the file's own settings, or whether an assignment was made by hand or by a rule. Export says which of those it left behind rather than dropping them quietly. Whether the original recognises an item it has already imported, and exactly what its own Export writes, are not yet measured.

2026-10-03 13:34 -0600 · Users Guide § 2.25 · oracle run 23

The filter editor, and SAGE-11's closing condition met

The Filter box IS the category tree, the same widget the conditions and actions boxes use, and what SPACE does depends on the row's type: a standard category cycles three positions in place, a date or numeric one opens a box with bounds. The engine had modelled every part of this since the range-filter unit, so this added no engine code at all — it is purely the surface that was missing, and it is the last piece of "define a view".

A draft of the notes called this "its own ticket rather than a thing bolted onto this panel", while SAGE-11's own step 3 had already enumerated filters as in scope. The claim read plausibly from the finished screen's point of view and contradicted the ticket. A unit's scope is the ticket's sentence, not the shape of the screen you just finished — "this is a distinct surface" is an argument about implementation that was quietly answering a question about scope.

2026-09-17 18:53 -0600 · Users Guide § 2.54 · oracle run 47

A section you can configure, and the columns

F6 with the cursor on a section head opens that section's properties — whether it expands to a section per child, and the columns. One measured key now reaches three screens, and which one opens is decided by what panel is already up and which row the cursor is on.

A review caught the write path editing the WRONG section. A section's identity is a slot, which counts expanded heads, and the panel looked it up in the raw definition list, which does not — so standing on one section drew and edited another, with a success message and no error. The lesson is not "index carefully": the right rule was applied at the capture and the wrong one at the resolution, and a rule that names only where an identity is CAPTURED will be obeyed and still break.

2026-09-17 18:43 -0600 · Users Guide § 2.47 · oracle run 42

The keys the original already told us

F6 opens Properties in both managers, which is what the original's own function-key bar says it does. Three other keys this shell had been calling inventions — F8 for the View Manager, F9 for the Category Manager, F6 for Properties — were printed on that bar all along, transcribed in the research doc since the run that read it.

Every one of those keys was already written down, and the reason they shipped marked as guesses is worth more than the keys: each cited a NEARBY measurement answering a different question, then read the absence of an answer in that section as the absence of one anywhere. Before marking anything as this engine's invention, grep the research doc for the surface's own name.

2026-09-17 18:22 -0600 · Users Guide § 2.42 · oracle run 40

A view you can name and configure

Rename a view and set what it hides, from inside the View Manager, on whichever view the highlight names rather than only the one on screen. The properties form is now one piece of code serving both managers — the same keys mean the same things whether you are editing a category or a view, because there is only one implementation of them.

Enforcing "every view must have at least one section" as a STATE rather than as a change locked a view that already had none out of all editing — renaming it was refused with a message about a field nobody had touched, and nothing in the app could give it a section. A rule about what a write CHANGES is not the same rule as one about what it leaves behind, and the difference only showed up because the file format is deliberately more permissive than the editor.

2026-09-17 18:20 -0600 · Users Guide § 2.47 · oracle run 35

More than one view, and a way between them

F8 lists every view in the file and switches to the one you pick — or makes a new one when you type a name that matches nothing. A view is the fourth primitive and the one the whole design turns on: the same items, demarcated and annotated differently. Until now the app could show exactly one, built in code, with no way to make a second or reach it.

The shell had been holding a copy of the view it was drawing, so an edit to the stored definition never reached the screen — a bug that predates this feature and that only building switching surfaced. It resolves the view from the database on every read now, which is the same correction three other parts of this shell have already needed.

2026-09-17 17:57 -0600 · Users Guide § 2.47 · oracle run 42

Tier 2 — The rules

Built

Conditions, actions, the assignment queue with cascade, Initiative and Authority, and natural-language date parsing.

A category can export each item that reaches it, the way the original does

A category set to "Export item" now writes a copy of each item the moment it is filed there, by hand or by a rule. Running the original showed it appends each one to a single export file rather than replacing it, keeps only the filings made by hand plus the dates, and writes them in the order the item got them. ALT-X saves that file. The same run gave us the first file the original itself had ever written for us, and reading it back found two places this app misread the original's dates; both are fixed.

When the original recognises a date in an item's text, it writes the words it read as well as the date. This app does not keep those words, so its export writes the date alone. Dates are written month first, the only order measured.

2026-10-03 19:55 -0600 · Users Guide § 2.63 · oracle run 56

A category's actions can be switched off, and the file keeps its own settings

Every category, and the file as a whole, has a switch for its actions: whether assigning an item to it also files the item somewhere else. The running original showed the switch belongs to the category that owns the action, so turning a parent off also stops the action its subcategories inherit, while a "discard" or "mark done" action ignores it. The file's own settings, how eagerly it files things, whether it asks first, and how it reads dates, are now saved with the file instead of quietly resetting to the defaults.

Whether the switch also stops an action that sets a date or a number was not read off the original; this app treats it the same way. Nothing on screen changes these settings yet.

2026-10-03 18:42 -0600 · Users Guide § 2.62 · oracle run 54

Four readings from the original reverse four of our guesses

A filter on a range of dates or numbers now narrows a view like any other filter, where it used to widen it: checked item by item, the original shows only what passes every filter. A confirmation setting no longer turns a date rule's match into a question, so something due today simply lands in its category at midnight. A question waiting for your answer is now saved with the file and is still there tomorrow, as it is in the original. And the trash empties itself at the end of the day, live at midnight or the next time the file opens, while today's discards stay recoverable.

The emulator's own clock was moved to cross midnight, never the computer's. The trash's three other emptying settings are not built, and what the original does with a question about an item that is then thrown away was not read; this app drops the question.

2026-10-03 16:43 -0600 · Users Guide § 2.60 · oracle run 51

Search from the keyboard, and type into what it found

ALT-S opens the search box in both versions of the app, and ENTER shows what it found as a view of its own. Running the original settled what we had guessed: its search treats "cover", "covered" and "covering" as one word, matches whole words only, and has no special rule for names. Typing into the result adds an item that stays listed even if it does not match, filed nowhere until a rule takes it, exactly as the original does.

The result view lasts for the session, where the original keeps it in the file and asks before a new search replaces it. Seven neighbouring commands on the original's Show menu are still unread, and whether its search folds accents is not yet measured.

2026-10-03 14:16 -0600 · Users Guide § 2.58 · oracle run 49

Categories are editable, not just creatable

Rename a category, give it a short name or an also-match list, set its special action, or make a family exclusive — from the keyboard, in either flavor. Making a family exclusive after the fact reconciles the items that already conflict, moving them to the parent the way the original's own dialog does, and says how many it moved.

Renaming one of the three built-in date categories broke the engine outright: four separate places look those up by a hardcoded name, so a rename made the next item typed throw and a saved rename silently split the date model in two. It is refused now. A review found it by hand-tracing, with no ability to run anything — five findings, all five reproduced exactly when the snippets it supplied were run.

2026-09-17 17:31 -0600 · Users Guide § 2.10 · oracle run 9

The hierarchy, on screen and navigable

F9 draws the whole category tree with its properties beside it. Type to locate a category anywhere in the hierarchy, collapse and expand families, and read what each one matches on. Until now the tree existed only in the file — you could create a category by naming it, and never see where it sat or what it was configured to do.

Two keys the app binds were keys the browser never handed over: ALT-minus and ALT-equals are not letters, so the guard deciding which keystrokes reach the app refused them both, in silence, with every test passing. That is the second time this exact seam has produced a bug — no unit test can see it, because the tests drive the app directly and the fault is at the browser boundary. Building it, serving it, and pressing the key is what found it, both times.

2026-09-17 17:05 -0600 · Users Guide § 2.12 · oracle run 10

A picker for the whole hierarchy, not just the matching part

F3 opens the category tree over the prompt. Every category is drawn and the ones that do not match are dimmed rather than hidden, so you can see where a name sits in the hierarchy while you narrow toward it — which is the difference between choosing from a list and navigating a structure. Four separate rules decide where the highlight lands, and typing a name nothing matches creates it instead.

One of the four rules shipped wrong and the test written from the measurement is what caught it: emptying the search was applied on every redraw rather than only on the keystroke that emptied it, so the arrows could not move at all. The measurement's own second clause said otherwise. Five tests on this ticket have now passed for a reason other than the one they name.

2026-09-17 16:34 -0600 · Users Guide § 2.56 · oracle run 48

The engine's guesses become something you can watch and steer

Press ALT-M on an item and type: a line beneath the prompt counts what matches and names which category will be assigned, stepping through the candidates as you narrow it. The same completion runs inside a column cell. This is the product's own thesis made visible — you do not file the item, you name a category and the engine finds it — and until now the machinery to do it existed with no key that reached it.

Two bugs here were invisible to every test and turned up only by opening a browser and pressing the key. Both UI flavors kept a stale copy of the guard deciding which keys the app sees, so the new shortcut did nothing at all, in silence; and a later review showed that swapping the resolved key name for the raw one survived the whole suite. The decision now lives in one place that the flavors cannot second-guess.

2026-09-17 13:08 -0600 · Users Guide § 2.56 · oracle run 48

Type a category name, and the column stops inventing categories

Agenda completes a typed category name against every category whose whole name it is a case-insensitive prefix of, listing the matches alphabetically and assigning the first one unless you step to another. Measuring that turned up a bug nobody had filed: the column cell resolved only an exact child name and created a category for everything else, so typing two letters over an existing name made a second category rather than assigning the one there. Every prefix was a new category, and no test caught it because every name the tests typed was already unique or exact.

Pressing F5 over a name a sibling already holds is refused here, where the original makes a second category with the same name. Assigning the existing one instead would let a key labelled NewCat fire that category's actions and discard the item, which an adversarial review traced before this shipped. The review also found the prompt matching the raw typed text while trimming everywhere else, so a leading space re-created the same stray-category bug one layer up.

2026-09-17 12:28 -0600 · Users Guide § 2.56 · oracle run 48

A standing suggestion survives a day only while it still matches

The previous unit's merge kept every suggestion the day did not answer, so a question earned by a date that has since passed stood one keystroke from an explicit assignment the item no longer earned, and one earned by a note outlived the note edit that ended it. The engine already asked the right question of a held assignment at every moment, whether the text still matches and the conditions still hold and how the two are weighed, and the queue now asks the same one of a suggestion, from one definition rather than a second rule. A cross-model review had flagged the first case a day earlier and its report never reached the session; a read of the reviewer's own logs recovered it.

Whether the original keeps a pending question across midnight was still unmeasured here; a later run (section 2.60) measured that it does. The review of this unit found the circular-category refusal pinned by a test that could not tell it from refusing every conditions-bearing suggestion, which the same moment happens to re-produce; two keep-tests now can.

2026-09-17 10:52 -0600 · Users Guide § · oracle run

A section is its slot, the sweep re-anchors the cursor, and the queue survives a day

The two follow-ups the driver's review surfaced, and what sat under them. An open entry names its section by its place in the view rather than by a position that a hidden section appearing above it could shift, and two sections may share a head with different filters, so neither the position nor the head was the identity. The cursor is a row index, and the day's sweep is the first write that runs with no key to place it afterwards, so after a sweep the cursor is found again by what it was on. And the sweep was emptying the confirmation queue every morning, since a moment that does not change an item's text cannot re-suggest what the text earned; the queue now keeps what stands and adds what the day brought.

Whether the original keeps a pending question across midnight was unmeasured when this landed; keeping it loses nothing, and a stale one is one keystroke from skipped. A later run (section 2.60) measured that it does, and that the file stores it. The cursor rule and the slot are this app's answers to a situation the original, on a single machine's clock, never had to state.

2026-09-17 08:28 -0600 · Users Guide § · oracle run

The fourth moment is driven — the day rolls over and the rules run

The engine had answered "the system date changes" for weeks and nothing called it. Both flavors now share one clock: the local calendar day, checked once a minute, again when the tab comes back, and once at open, since a browser closed tonight and opened tomorrow is the commonest case. The sweep feeds the confirmation queue, so a match a relative bound now satisfies becomes a question rather than being lost, and the file is saved only when something in it actually changed.

Whether the original re-evaluates at a live midnight, or at startup, was unmeasured when this landed: the emulator takes its clock from the host. The cadence and the local-day rule are this app's choices, and the review that shipped this found the sweep to be the first write that happens outside a keystroke, which two follow-up fixes answer. A later run (section 2.60) measured both: it does, at midnight and at startup — and a match a date condition earns is made outright, not turned into a question.

2026-09-17 07:28 -0600 · Users Guide § · oracle run

Six readings from the emulator, four of which overturned the engine

Run 47 read a date-range filter as OR'd with the rest of the view, where the engine had ANDed it and pinned that by a test; a later run reversed that reading. A date column refuses what it cannot read as a date, where the engine stored it. An item typed into a section whose head discards it never reaches the trash, while a discard fired on an existing item goes through it, and recovery re-runs text matching but never restores the assignment that did the discarding. Three more readings confirmed the engine as built. Runs 45 to 47, sections 2.50 to 2.55.

Whether a numeric range is OR'd the same way is inferred from the manual describing both in the same words, and whether the original breaks the discarding assignment at discard or drops it at recovery cannot be told apart on the screen. Both are filed as probes. A later run (section 2.60) overturned the first reading: checked item by item, a range filter ANDs with the rest of the view, date and numeric alike.

2026-09-16 19:36 -0600 · Users Guide § 2.54 · oracle run 47

A review of the day's work — one crash, and thirteen tests that could not fail

The eight units above were reviewed by reverting one line at a time and watching whether any test noticed. Of 108 reverts, 94 were caught; thirteen were real gaps, each now pinned by a test that fails under the revert. The review also found a crash the reviews of the units had not: a section or a column that discards what is typed into it threw an error out of a keystroke. Fixed as a third reason an item can vanish at a moment.

What the original does with an item typed into a section whose head discards it was unmeasured when this landed. Not crashing is right whatever it does; the next unit measured it, and the item never reaches the trash at all.

2026-09-16 14:30 -0600 · Users Guide § · oracle run

Per-category settings

Every rule about how eagerly the engine files can be set for one category instead of the whole file — how close a match has to be, whether to ask first, what to do about a conflict. We had modelled all of it as file-wide.

One row of that screen, switching a category's actions off, waited on a design decision. It arrived later, with the file's own settings saved in the file.

2026-09-16 12:30 -0600 · Users Guide § 2.39 · oracle run 37

Show Match is a search, and three comments said otherwise

This unit began by deleting claims rather than adding a feature. Three places in the code asserted that a menu command reported why an item matched. Opening the menu showed it is a plain text search. The interesting part is what it produces: a VIEW, which is one of the four primitives — a search is not a new kind of screen here.

Where the original explains a match, if it does anywhere, we have not found. Seven neighbouring commands on that menu are unread.

2026-09-16 12:13 -0600 · Users Guide § 2.45 · oracle run 42

The day rolling over is a moment too

The fourth and last of the moments the design names. It has anything to do at all only because of a measurement: type "today" into a date condition and the original stores the WORD, not the date. The rule means a different day tomorrow.

The engine answers the moment; nothing drives it yet, because deciding when the day has turned is a question the app layer has to own.

2026-09-16 11:59 -0600 · Users Guide § 2.49 · oracle run 44

Authority — it asks instead of assuming

Half of the name. Turn confirmation on and the engine stops filing on a guess: it queues the guess, shows you a question, and what you confirm becomes permanent in a way its own guesses never are. The queue had been finished for weeks with no way to see it.

The keyboard shortcut is ours. In the original this is menu-only — we checked, and the neighbouring command advertises a shortcut while this one does not.

2026-09-16 11:48 -0600 · Users Guide § 2.16 · oracle run 14

Conditions over dates and numbers

"Due in September" and "costs more than 500" are conditions, and they turn out to have exactly the same shape as the filters that decide what a view shows. One rule, asked in two places.

2026-09-16 11:22 -0600 · Users Guide § 2.44 · oracle run 42

Changing a rule does not re-file what you already have

Give a category a new condition and the items already in the file are left alone. That sounds like a missing feature and it is the design: re-filing the past is a separate, deliberate command. We had built nothing here, and the original does nothing here.

2026-09-16 10:50 -0600 · Users Guide § 2.46 · oracle run 43

A parent category's actions apply to its children

An item filed under a child fires the parent's rules. We had it wrong, and the reading that settled it was a negative one — an action REMOVING an assignment, which no competing explanation could have produced.

2026-09-16 10:43 -0600 · Users Guide § 2.43 · oracle run 41

The rules reach the app

The engine had held all of this for weeks and the app could reach one key of it. Typing an item assigned nothing — the whole premise, that it files for you from the text you typed, was not true of the running app.

Three of the four moments the design names are wired. The fourth is the system date changing, and nothing decides when the day has rolled over yet.

2026-09-16 10:18 -0600 · Users Guide § 2.42 · oracle run 40

The note editor, and the moment that comes with it

A note is one of the four primitives, and editing one is a moment the rules run at. Measured on the original: a category that matches a word in your note claims the item when you edit the note, and ignores it when you edit the item's text.

2026-09-16 00:11 -0600 · Users Guide § 2.18 · oracle run 16

Tier 3 — The inference

Building

Update-through-views and query synthesis. The 1990 paper states the goal and declines to explain the method; the manuals and the running original turned out to answer most of it. Query synthesis is the rule the filters already follow, and deleting a category is built to what the original does. What is left is two readings of the original, each backing a choice this app has so far made alone.

A category can be thrown away, and the views that named it survive

In the Category Manager, ALT-F4 (or DEL) throws a category away, with everything filed beneath it, after one question. Running the original showed what happens to the rest of the file: every mention of the category is quietly dropped from the views and from the other categories' rules, and no item is lost. A view left with nothing to show asks, the next time you open it, whether to throw it away too, and keeps asking until you do or give it something to show.

The original was not watched dropping a category from a column, from a rule's condition, from a pending question, or from the trash. This app drops it from all of them the same way, which is the only choice that keeps the file loadable.

2026-10-03 21:32 -0600 · Users Guide § 2.64 · oracle run 58

Query synthesis was already here, and a confirmation the original refuses

The 1990 paper's last unexplained piece, query synthesis, turned out to be a rule the filters already follow: nobody means a query that must come back empty, so picking categories that can't share an item gives you either one. What is left is deleting a category. The same run found a category set to refuse hand-made assignments also refusing a confirmed suggestion outright, where this app had kept it; it now refuses too, and the question comes back the next time the item's text changes.

What a view does when a category it names is deleted is unread, and so is the setting that appears when a category refuses hand-made assignments, "Make items fit the category settings".

2026-10-03 17:23 -0600 · Users Guide § 2.61 · oracle run 52