Closed IA with fast decisions
September 20, 2026 · View on GitHub
Cross-domain update: The default now uses neutral responsibilities and has been exercised against
all 30 src/probe/custom.ts prompts. See the sample-sweep report for the
fixed-rubric comparison, every sentence, limitations and reproduction commands.
Construct-and-critique experiment: npm run probe:ia:whole now constructs one complete design,
then critiques its concrete journeys. It no longer uses per-edge confidence gates. See
One resolved design below. Earlier sections and recordings document the
previous experiments; npm run probe:ia still runs the original first-screen probe.
npm run observe:journeys # http://127.0.0.1:5174
npm run probe:ia # captured podcast example, Jev
The observer now opens on the app map: candidate vocabulary, decision batches, canonical aliases,
remaining conflicts, mock bindings and replay. Select destinations or their actions to follow the
map; select a batch to inspect an earlier state. Every model request and distribution is recorded.
The earlier transition experiments remain at /transitions.
This experiment makes no Gemini calls and no model calls during navigation. Code creates the
finite candidate set; Jev or Gev answers Choice questions; code assembles the map and checks it.
--endpoint=gev selects Gev when configured. --batches=1 caps work at one request; the default
allows at most three batches, with only unresolved decisions asked again after the first batch.
What enters the decision model
The original brief, the original screen's title/archetype and control labels, and its observed actions. Code extracts this evidence from the saved snapshot. Later screens, analyst expectations, original generation judgments and fake sample values are excluded. Both supplied podcast captures produce identical first-screen evidence.
Code enumerates first, a possible home, and one candidate per action that might open a screen.
Toggles and check controls already known to act in place need no semantic decision. Labels come
from the existing mock. Candidates are not recursively expanded. The budget is 24 candidates and
30 observed actions; exceeding it fails explicitly. The current extractor handles app bars, nav,
grouped settings, lists and actions. Forms and custom components require further extraction work.
One batch asks where each action leads, what task a distinct destination serves (picker, form, confirmation, detail, overview or outcome), how it relates to the first screen, and whether Home is separate. Conditional answers are read only when applicable. Code uses these decisions to:
- Merge identities and keep
firstattached to the original mock. - Suppress a redundant self-link while recording its disposition.
- Add return/completion/in-place actions from a finite grammar.
- Give Home an entry to the existing first destination.
- Reject dangling targets, missing action coverage, unreachable destinations, unsupported intent, contradictory parent identities and unresolved decisions.
A choice needs probability ≥0.65 and a ≥0.15 lead over alternatives. These are experimental routing
thresholds, not calibrated guarantees. Uncertain choices and contradictions can trigger a targeted
follow-up batch. Settled answers stay fixed. Repeated unresolved results stop, and the map remains
needs_review; there is no text-generation fallback and no unbounded growth.
Replaying the two reported paths
The original mock is bound once to first. The replay harness maps the recorded gear and Back taps
to observed source-link IDs. Home's emitted entry to Settings already carries target: first.
The saved tab label is checked against that emitted entry; an unknown label stays outside the map.
In an integrated renderer, links would carry these canonical IDs directly.
Home's held-out saved mock #4 stands in for first materialization after planning. No fresh Home mock is generated by this probe. Settings mock #2 and #5 are retained as evidence of the original behavior, but the corrected replay never binds either of them.
On 2026-09-20, the revised decision wording produced:
| Operation | Result |
|---|---|
| First batch | 26 questions, 403 ms; one outstanding Home decision |
| Targeted second batch | 1 question, 118 ms |
| Final map | 9 destinations; every observed action accounted for |
| Settings gear | Redundant; remain on original mock #1 |
| Settings → Home | Bind saved Home mock #4 for replay |
| Home → Settings | Reuse original mock #1 |
| API calls | 2 decision calls, 0 generation calls, 0 navigation calls |
The two calls used 20,401 input tokens as reported by the service. The time is request duration, not a guarantee for future runs. This was Jev; Gev is not configured locally. The first decision-only attempt remains recorded: vague candidate descriptions confused the location of a control with the screen it opens, and left seven routes unresolved. The revision explicitly distinguishes opening one picker from returning to the entire overview, using the renderer's existing row-control semantics. Thresholds were not lowered to obtain a passing run.
Scope and next experiments
This is a first-screen IA experiment and deterministic replay, not a production routing integration. The candidate grammar covers destinations implied by observed actions plus Home, returns and task completion. It does not invent an exhaustive podcast product. Planned destination mocks have not been rendered, and their future controls have not been audited. If new controls need destinations outside the map, that must become an explicit map revision.
stable means closure and consistency under this grammar and the selected decisions, not proof of
semantic coherence for arbitrary apps. The Settings gear identity is a model decision. The reverse
entry from Home is a code rule. The current experiment demonstrates their composition on these
captures; it does not independently prove general semantic matching across arbitrary entry labels.
Checks
npm run typecheck
node --import tsx --test src/server/ia/decisions.test.ts src/probe/journey-observer.test.ts src/probe/saved-journey.test.ts
Tests exercise reuse of the exact same mock through repeated returns, preserving legitimate picker navigation, explicit unsupported intent, targeted follow-up selection, broken graph detection, and exclusion of future mocks/sample values from the planning evidence. Browser checks cover the interactive map, revision selection, decision effects, replay, saved evidence and mobile layout.
Whole-app expansion and pruning experiment
npm run probe:ia:whole # or npm run probe:ia -- --whole
This separate experiment starts with the first-screen closure, then expands into twelve code-authored, domain-neutral responsibilities: Browse, Search, Library, Collection detail, Item detail, Active experience, Queue, Downloads, Activity, Profile, Create or book, and Help. These are hypotheses for the surrounding app, not requirements extracted from later saved screens. The original 24-destination graph budget still applies; excess candidates fail explicitly.
Jev decides whether to keep, omit or merge each responsibility. Code redirects incoming links to
canonical IDs and suppresses self-links. Retained responsibilities and Home get independent decisions for each possible directed link:
include, omit, or unknown. Several outgoing and incoming links can coexist. Existing observed
first-screen actions are preserved, and setting pickers stay under their original overview.
Local action grammar is decided separately. Scope permits one targeted retry; unresolved links
receive a task-consequence assessment instead of repeating the original inclusion question.
Unsettled local-grammar questions can share that assessment batch.
When those settle, a second scope critique sees the connected map and can prune it further.
The final coverage question always sees the map after pruning. Missing journeys, ambiguous choices,
and unreachable nodes prevent stable; structural closure alone cannot pass the experiment.
The maximum is nine requests including the three-request first-screen budget. Navigation stays
local, and no mocks or free-form text are generated.
The observer labels expansion, pruning, connection and audit revisions separately. It shows total probe wall time alongside summed decision request time. Synthetic screen labels are responsibility names; they are not finished screen designs. The original run below used a primary-entry/primary-next grammar; the current version removes both exclusive choices. Link membership does not compete with link presentation (such as whether a control looks like Back). Included directed pairs are navigation edges. Unresolved choices are retained visibly, not silently decided by argmax or removed just to make the graph pass.
First measured run: 2026-09-20
Recording: 2026-09-20T17-39-44-725Z-fc405993.
| Stage | Questions | Request time |
|---|---|---|
| First-screen decisions | 26 | 439 ms |
| Scope pruning | 12 | 197 ms |
| Resolve scope disagreements | 5 | 169 ms |
| Connect core journeys | 18 | 193 ms |
| Resolve journey disagreements | 6 | 157 ms |
| Final coverage audit | 1 | 152 ms |
Total: 1.31 seconds in six Jev requests, 1.39 seconds probe wall time, 53,183 reported input
tokens, zero generation calls, zero navigation calls. One sample is not a latency benchmark.
The map expanded from 9 to 21 destinations, then pruned to 15. It finished needs_review, with
eight unsettled semantic decisions, one unreachable Downloads destination, and a final coverage
judgment of missing (0.66). The connected scope critique was skipped because earlier decisions
were still unsettled. Navigation replay was deliberately withheld, producing three outside-map
results rather than presenting the unfinished map as usable.
The useful result is that speed is plausible but convergence is not established. Library's forward step split between item detail and playback; Downloads' parent split between Home and Library. Repeating those exclusive questions did not settle all choices. A follow-up should test independent edge decisions or explicit app-scope constraints, rather than lower confidence thresholds. This catalog is a bounded whole-app hypothesis, not an exhaustive generator for arbitrary app domains. Production routing remains unchanged.
Independent links revision
connectionStrategy: independent-links identifies the revised recordings. Each connection revision
stores every pair's source, destination, status, model choice and selected probability. Supported
include and omit decisions are distinct from low-confidence or unknown answers. The observer draws
unresolved pairs as dotted orange lines and lists them separately from executable actions in the
selected destination's inspector. Omitted pairs remain inspectable in its link-decision table.
The original fixed screen and existing home entry are not re-decided by this experiment.
Recording 2026-09-20T17-48-21-619Z-9cc3d4ba took 1.75 seconds total, including 1.63 seconds
in seven Jev calls, and reported 80,135 input tokens. The connection stage asked 48 independent
pair questions plus six local-grammar questions; its retry asked 29 unsettled questions. Library →
Item detail was included at 0.68; Library → Active experience remained unresolved at 0.49, separately
from the item link. No threshold was lowered. Six pairs were included, nineteen omitted and
twenty-three unresolved. The full map remains needs_review; the strategy correction does not
establish whole-app coherence. Independent pairs increase question count quadratically with the
bounded responsibility set, while questions within a request are evaluated in parallel.
An intermediate run (2026-09-20T17-47-45-918Z-7234ae55) also remains available. It unnecessarily
made “navigate” and “back” competing choices within each pair. The final revision asks only whether
the link belongs, leaving its control presentation out of that decision.
Tests cover coexisting Library → Item detail and Library → Active experience links, multiple entry points, independent reverse links, unresolved versus intentionally omitted pairs, preservation of observed actions, and deterministic rebuilding without duplicate links.
Task-consequence decisions
refinementStrategy: task-consequences adds a decision layer that can change the map, not just
annotate uncertainty. Code finds existing supported routes for each unresolved pair and calculates
which destinations would become reachable from Home if that link were added. It also records which
endpoint responsibilities remain unsettled. Pending links are never used as supporting paths.
Jev receives that evidence and selects a task consequence:
| Supported consequence | Graph effect |
|---|---|
| Required for a core task or return | Add the directed link. |
| Optional shortcut with a sensible existing route | Defer it without a blocking finding; record the alternative. |
| Unnecessary direct transition | Omit the link while keeping both destinations. |
| Unsettled responsibility | Keep the link pending and flag the responsibility disagreement. |
| Unknown or insufficient probability/margin | Keep the consequence explicitly unassessed. |
The existing 0.65 probability / 0.15 margin rules still apply. They are experimental thresholds, not calibrated reliability estimates. A path alone never establishes that a journey makes sense; that requires the semantic decision. Conversely, even a confident “optional” judgment cannot defer a shortcut without an actual supporting route in the input map. Simultaneous additions cannot justify one another's deferrals. If later pruning breaks an alternative, its optional decision is reopened. Raw inclusion distributions, consequence distributions, evidence and effects are retained.
The observer replaces the aggregate finding card for these runs with categories for journey gaps and coverage, responsibilities to settle, optional links, and unassessed links. A journey-gap count counts checks, not distinct user journeys; responsibility counts also include local-grammar and link-related scope questions. Optional shortcuts appear separately from executable actions with an explicit route. Remaining unknowns do not get mislabeled as optional.
The first measured run, 2026-09-20T17-56-20-084Z-81bc002a, took 1.89 seconds total, including
1.77 seconds in seven Jev calls, reporting 88,210 input tokens. The consequence batch contained
27 link assessments and one local-grammar follow-up and took 353 ms. It settled Downloads →
Browse as unnecessary (0.69), reducing unresolved pairs from 27 to 26. No required or optional link
judgments cleared the cutoff in this run; the map remains needs_review. The run is not evidence
that this layer guarantees convergence, and re-running earlier stages means it is not a controlled
comparison with previous recordings. There were zero generation and navigation model calls.
Tests exercise required-link insertion, optional deferral with a valid alternative, independent omission, preservation of original mock actions, unresolved responsibility judgments, rejection of circular justification across a batch, and reopening an optional link after its alternative breaks. Browser validation covers recorded category/effect changes and historical compatibility; the optional-shortcut display is tested using an in-memory fixture, not passed off as a live outcome.
One resolved design
The target is one possible fully resolved IA, not an ideal or uniquely correct product. The
current probe:ia:whole uses refinementStrategy: construct-and-critique and works as follows:
- Resolve the original screen's observed controls with the existing first-screen probe, preserving its canonical mock binding. This stage still uses its original confidence checks.
- Ask Jev to choose which of nine proposed surrounding tasks belong in this design. Apply the returned include/omit choices directly; distributions are recorded, not used as confidence vetoes.
- Code instantiates a complete map from finite task templates, including local actions and return links. Shared destinations survive scope pruning when another task uses them. No independent edge is waiting for a probability threshold.
- Critique actual executable journeys and an app-wide coverage question in one batch. A working journey stays even when alternatives exist. A critique can request a specific shortcut or remove an out-of-scope task. Changed journeys and coverage are assessed again, up to three critiques.
- Keep structural resolution separate from semantic review.
resolvedmeans all observed actions have dispositions, destinations are reachable, and every navigation action has a valid target.proposalSummary.reviewedadditionally requires the recorded journey and coverage critique to pass. Unknown or missing-coverage judgments remain explicit review notes. They do not turn chosen links back into dangling edges or disable deterministic navigation through a valid proposal.
The observer shows complete paths, end activities, return paths, scope choices, critique probabilities and specific revisions. It distinguishes graph resolution from review findings. No threshold was merely lowered: whole-app scope is now a design choice, routes are code-authored hypotheses, and semantic decisions review complete experiences. A model's “works” cannot override missing routes, return paths or required local actions. No unreviewed last-round repair is applied.
Measured result
Recording 2026-09-20T18-06-45-714Z-ac761289:
- 18 destinations, 59 action dispositions, zero unresolved graph links.
- Six selected tasks: discovery, search, saved content, downloaded content, recent activity and account/settings. Queue management, creation/booking and help were left outside this design.
- Library → Item detail → Active experience is a concrete chosen path; its critique said works. All six proposed journeys received works judgments. Their probabilities remain inspectable.
- App-wide coverage still received missing (0.65). The run is structurally resolved, not certified semantically complete. That recorded finding is not hidden or changed into an accepted judgment.
- All three saved Settings checks passed, including Home → Settings reusing original mock #1.
- 0.88 seconds total; 0.82 seconds across four decision calls; 31,132 reported input tokens. Two first-screen batches, one scope-selection batch, one journey-critique batch. Zero generation calls and zero navigation model calls.
The earlier unscoped complete proposal (2026-09-20T18-04-30-363Z-57369449) remains recorded. The
final version adds explicit scope selection so a generic capability catalog is not automatically
an app's feature list. Neither recording is a performance benchmark or evidence of optimal IA.
This remains an IA experiment: planned mocks are not rendered, and production routing is unchanged. The route grammar uses generic responsibilities and is bounded to 24 destinations. It assumes the original screen's existing actions can enter the surrounding app; it cannot silently add new controls to an immutable first mock. Unsupported cases surface structural checks. Code templates are an explicit source of design choices, so critique quality and broader domain coverage remain experimental rather than established by closure alone.
Additional tests cover complete drafts before critique, preservation of the original screen, low-confidence but valid design choices, executable shortcut repair, pruning without losing shared responsibilities, missing local actions/returns, and review uncertainty without unresolved edges.