Files
ARCADIA-Studio/harness/experiments/text-navigation-components-20260911/native-preparation/prepare.py
T
sorokin.oleg.edu 36db9356cc 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.
2026-09-11 11:23:20 -07:00

99 lines
4.7 KiB
Python

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)}))