Add bounded definition and reference navigation for current source drafts

Preserve loaded draft bytes and model history, reject stale workspace and input events, and keep captured Escape under modal priority. Verified 181 focused Windows tests and strict desktop Clippy on exact inputs4; earlier full737 scope and component/native preparation evidence are archived separately. Native navigation acceptance remains pending.
This commit is contained in:
2026-09-11 11:23:20 -07:00
parent fb24def2f5
commit 36db9356cc
208 changed files with 44983 additions and 62 deletions
@@ -0,0 +1,98 @@
from pathlib import Path
import hashlib
import json
import shutil
ROOT = Path(__file__).resolve().parent
REPO = ROOT.parent.parent
def write_source(relative, text):
path = ROOT / relative
assert not path.exists(), path
path.parent.mkdir(parents=True, exist_ok=True)
path.write_bytes(text.replace('\r\n', '\n').replace('\n', '\r\n').encode('utf-8'))
files = {
'project.arcadia': '''arcadia 7;
project navigation_trial "Navigation — 救援" id "9e8f0000-0000-4000-8000-000000000001" {
description "Prose @assess must not be a lexical use.";
include "救援/assess.arcadia";
include "救援/confirm.arcadia";
include "links.arcadia";
}
''',
'救援/assess.arcadia': '''// Comment @assess must not be a lexical use.
oa_activity assess "Assess 救援 request" id "9e8f0000-0000-4000-8000-000000000002" {
description "Keep @assess as prose, not a use.";
}
''',
'救援/confirm.arcadia': '''oa_activity confirm "Confirm 救援 response" id "9e8f0000-0000-4000-8000-000000000003" {}
''',
'links.arcadia': '''interaction handoff "Assessment handoff" id "9e8f0000-0000-4000-8000-000000000004" {
description "救援 @assess is prose"; source @assess; target @confirm;
// Another comment @assess is excluded.
}
''',
}
for name, text in files.items():
write_source('valid-baseline/' + name, text)
shutil.copytree(ROOT / 'valid-baseline', ROOT / 'valid-work')
for case in ['ambiguous-open', 'missing-include-open']:
for name, text in files.items():
if case == 'ambiguous-open' and name == '救援/confirm.arcadia':
text += 'oa_activity assess "Duplicate draft assessment" id "9e8f0000-0000-4000-8000-000000000005" {}\n'
if case == 'missing-include-open' and name == 'project.arcadia':
text = text.replace(' include "links.arcadia";', ' include "links.arcadia";\n include "missing/not-loaded.arcadia";')
write_source(case + '/' + name, text)
# A realistic existing teaching model, copied without changing source bytes.
shutil.copytree(REPO / 'examples/release1', ROOT / 'release1-baseline')
shutil.copytree(ROOT / 'release1-baseline', ROOT / 'release1-work')
def position(raw, offset):
line_start = raw.rfind(b'\n', 0, offset) + 1
return {'byte_offset': offset, 'line_1based': raw.count(b'\n', 0, offset) + 1,
'utf8_byte_column_1based': offset - line_start + 1}
locations = []
for folder in ['valid-baseline', 'ambiguous-open', 'missing-include-open']:
for path in sorted((ROOT / folder).rglob('*.arcadia')):
raw = path.read_bytes()
text = raw.decode('utf-8')
assert b'\n' not in raw.replace(b'\r\n', b'')
for alias in ['assess', 'confirm']:
token = alias.encode('ascii')
start = 0
while (found := raw.find(token, start)) != -1:
start = found + len(token)
line_start = raw.rfind(b'\n', 0, found) + 1
prefix = raw[line_start:found]
# Explicit fixture expectations, independent of the application lexer.
if prefix == b'oa_activity ':
kind = 'declaration'
elif prefix.endswith(b'source @') or prefix.endswith(b'target @'):
kind = 'lexical-use'
elif found > 0 and raw[found-1:found] == b'@':
kind = 'excluded-comment-or-string'
else:
continue
locations.append({'fixture': folder, 'path': path.relative_to(ROOT / folder).as_posix(),
'alias': alias, 'kind': kind, 'span': [found, start],
**position(raw, found)})
expected = {'encoding': 'UTF-8 without BOM; every authored fixture newline CRLF',
'valid_totals': {'assess': {'declarations': 1, 'lexical_uses': 1},
'confirm': {'declarations': 1, 'lexical_uses': 1}},
'ambiguous_totals': {'assess': {'declarations': 2, 'lexical_uses': 1}},
'locations': locations}
(ROOT / 'expected-locations.json').write_text(json.dumps(expected, ensure_ascii=False, indent=2) + '\n', encoding='utf-8')
manifest = [{'path': p.relative_to(ROOT).as_posix(), 'bytes': p.stat().st_size,
'sha256': hashlib.sha256(p.read_bytes()).hexdigest()}
for folder in ['valid-baseline', 'valid-work', 'ambiguous-open', 'missing-include-open', 'release1-baseline', 'release1-work']
for p in sorted((ROOT / folder).rglob('*')) if p.is_file()]
(ROOT / 'fixture-manifest.json').write_text(json.dumps(manifest, ensure_ascii=False, indent=2) + '\n', encoding='utf-8')
print(json.dumps({'files': len(manifest), 'bytes': sum(p['bytes'] for p in manifest),
'expected_locations': len(locations)}))