fix(ui): name the shown operation in the Behavior Start field

Without a requested entry the journey opens with the first entry point
(Django: main in manage.py-tpl), so the heading read "What can main
call?" while the Start field still offered "Choose an operation...".
The field now follows the operation the journey shows.

Signed-off-by: Bernhard Jackiewicz <bj@techport.io>
This commit is contained in:
Bernhard Jackiewicz
2026-10-03 19:00:00 +02:00
parent f14674ebd5
commit 3f29dc5d5e
3 changed files with 13 additions and 2 deletions
@@ -110,6 +110,14 @@ describe('behavior journeys', () => {
await render(data, { data: undefined, pending: false, error: 'Snapshot changed.' });
expect(container.querySelector('[data-scene]')).toBeNull(); expect(container.textContent).toContain('Snapshot changed.');
});
it('shows the operation the journey opened with in the Start field, not "Choose an operation"', async () => {
// Without a requested entry the journey falls back to the first entry point (Django: main in manage.py-tpl).
await render(fixture(), { entryId: undefined });
const start = container.querySelector<HTMLSelectElement>('[aria-label="Behavior entry point"]')!;
expect(container.querySelector('.behavior-heading h2')?.textContent).toBe('What can operation1 call?');
expect(start.value).toBe('1');
expect(start.selectedOptions[0]?.textContent).toBe('operation1 · src/part1.ts');
});
it('pages only real edges and component lanes within the selected path window', () => {
const scene = behaviorJourney(fixture(), { entryId: 1, targetId: 9 }).scene;
const page = journeyPage(scene, 6), ids = new Set(page.nodes.map(node => node.id));
@@ -134,7 +134,8 @@ export default function BehaviorJourney({ project, generation, data, entries, ta
<p>{path ? 'Follow one recorded call chain across the parts it touches.' : 'Choose a starting operation. Explore its calls, or follow a path to a destination.'}</p></div>
<button onClick={onRefresh}>Refresh</button></header>
<div className="behavior-requests">
<label>Start <select aria-label="Behavior entry point" value={entryId ?? ''} onChange={event => request(availableEntries.find(item => item.id === Number(event.target.value)))}>
{/* The field names the operation the journey shows, also when the projection chose it. */}
<label>Start <select aria-label="Behavior entry point" value={entry?.id ?? ''} onChange={event => request(availableEntries.find(item => item.id === Number(event.target.value)))}>
<option value="">Choose an operation…</option>{availableEntries.map(item => <option key={item.id} value={item.id}>{item.name} · {item.file_path ?? item.qualified_name}</option>)}
</select></label>
<label>Reach <select aria-label="Behavior destination" value={targetId ?? ''} disabled={!entry || pending} onChange={event => request(entry, event.target.value ? Number(event.target.value) : undefined)}>
@@ -490,7 +490,9 @@ describe('system architecture workspace', () => {
expect(loader.mock.calls[1][0]).toEqual({ project: 'sample', entryNodeId: 2, expectedGeneration: 'analysis-g1', includeBehaviorEvidence: true });
expect(loader.mock.calls[2][0]).toEqual({ project: 'sample', entryNodeId: undefined, includeBehaviorEvidence: true });
expect(loader).toHaveBeenCalledTimes(3);
expect(container.querySelector<HTMLSelectElement>('[aria-label="Behavior entry point"]')?.value).toBe('');
// The stale choice is dropped; the Start field names the operation the reopened journey shows.
expect(container.querySelector('.behavior-heading h2')?.textContent).toBe('What can start call?');
expect(container.querySelector<HTMLSelectElement>('[aria-label="Behavior entry point"]')?.value).toBe('1');
expect(container.textContent).not.toContain('Entry snapshot changed.');
});
it('opens exact source witnesses and synchronizes selection by qualified name', async () => {