diff --git a/.github/workflows/english-gonol.yml b/.github/workflows/english-gonol.yml index 296367ce..d597ff4c 100644 --- a/.github/workflows/english-gonol.yml +++ b/.github/workflows/english-gonol.yml @@ -73,13 +73,24 @@ jobs: path: _deps/ucns-carrier persist-credentials: false + - name: Checkout pinned ordinal geometry producer + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + repository: The-Interdependency/ucns + ref: a7bfa0c96daca1f7445ba57c39fa6d8fd62e51eb + path: _deps/ucns-ordinal + persist-credentials: false + - name: Set up Python uses: actions/setup-python@v7.0.0 with: python-version: "3.12" - name: Install construction prerequisites - run: python -m pip install --disable-pip-version-check pytest PyYAML + run: | + python -m pip install --disable-pip-version-check pytest PyYAML + python -m venv _deps/ordinal-venv + _deps/ordinal-venv/bin/python -m pip install --disable-pip-version-check pytest PyYAML ./_deps/ucns-ordinal - name: Verify all pinned source identities run: | @@ -88,6 +99,7 @@ jobs: test "$(git -C _deps/oewn rev-parse HEAD)" = "dc343f2683279ecbb13fab4e2fd778d7b162d287" test "$(git -C _deps/edcm-metric-origin rev-parse HEAD)" = "0873c105799681ea1f7ccb3e619d1f7aebbd85e0" test "$(git -C _deps/ucns-carrier rev-parse HEAD)" = "4f863ad37096b7baab8f62820ad5cb937b62a3a7" + test "$(git -C _deps/ucns-ordinal rev-parse HEAD)" = "a7bfa0c96daca1f7445ba57c39fa6d8fd62e51eb" - name: Preflight full-corpus resources run: | @@ -105,9 +117,17 @@ jobs: --ucns-source-root "$GITHUB_WORKSPACE/_deps/ucns-carrier" \ --out-dir "$ENGLISH_GONOL_FULL_CONSTRUCT_ROOT" - - name: English Gonol construction tests + - name: Historical English Gonol tests + working-directory: research/english-gonol + run: | + python -m pytest -q tests \ + --ignore=tests/test_ordinal_construct.py \ + --ignore=tests/test_ordinal_definitions.py \ + --ignore=tests/test_active_construction.py + + - name: Isolated ordinal construction tests working-directory: research/english-gonol - run: python -m pytest -q tests + run: ../../_deps/ordinal-venv/bin/python -m pytest -q tests/test_ordinal_construct.py tests/test_ordinal_definitions.py tests/test_active_construction.py - name: Retain full-corpus metric-origin evidence uses: actions/upload-artifact@v7.0.1 diff --git a/.github/workflows/english-ordinal-affixiation.yml b/.github/workflows/english-ordinal-affixiation.yml new file mode 100644 index 00000000..80801322 --- /dev/null +++ b/.github/workflows/english-ordinal-affixiation.yml @@ -0,0 +1,213 @@ +name: English ordinal affixiation +on: + workflow_dispatch: + pull_request: + paths: + - 'research/english-gonol/english_gonol/**' + - 'research/english-gonol/ACTIVE_CONSTRUCTION.json' + - 'research/english-gonol/BASE.json' + - 'stack-manifest.json' + - 'STACK_MANIFEST.md' + - 'research/english-gonol/tests/test_active_construction.py' + - 'research/english-gonol/tests/test_ordinal_construct.py' + - 'research/english-gonol/tests/test_ordinal_definitions.py' + - 'research/english-gonol/tests/test_ordinal_source_inputs.py' + - 'research/english-gonol/docs/ordinal-*' + - '.github/workflows/english-ordinal-affixiation.yml' +permissions: + contents: read +defaults: + run: + shell: bash +jobs: + full-corpus: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + persist-credentials: false + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + repository: The-Interdependency/ucns + ref: a7bfa0c96daca1f7445ba57c39fa6d8fd62e51eb + path: _inputs/ucns + persist-credentials: false + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + repository: The-Interdependency/ucns + ref: 4f863ad37096b7baab8f62820ad5cb937b62a3a7 + path: _inputs/admission-carrier + persist-credentials: false + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + repository: globalwordnet/english-wordnet + ref: dc343f2683279ecbb13fab4e2fd778d7b162d287 + path: _inputs/oewn + persist-credentials: false + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + repository: The-Interdependency/skill-lib + ref: e74ce1ee8a2cea50b0a6acce7697a6d3d9b2e128 + path: _inputs/skill-lib + persist-credentials: false + sparse-checkout: | + wdll + gonol-build + msdmd + meta-module-build + interdependent-work-graph + tools/skill_usage.py + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 + with: + python-version: '3.12.14' + - name: Preflight resources and install exact build dependencies + run: | + python - <<'PY' + import json, pathlib, platform, shutil, sqlite3 + free = shutil.disk_usage('.').free + # Source, two verified SQLite bundles, dependencies and retained evidence. + assert free >= 4 * 1024**3, '4 GiB working space required before full build' + print(json.dumps({'python':platform.python_version(),'sqlite':sqlite3.sqlite_version,'free_bytes':free})) + PY + python -m pip install uv==0.11.18 PyYAML==6.0.2 + uv sync --project _inputs/ucns --locked --python python --extra test + uv pip install --python _inputs/ucns/.venv/bin/python PyYAML==6.0.2 + - name: Acquire declared Unicode release and license + env: + SOURCE_HEAD: ${{ github.event.pull_request.head.sha || github.sha }} + run: | + mkdir -p _inputs/ucd _evidence + for name in UnicodeData.txt DerivedCoreProperties.txt ReadMe.txt; do + curl --fail --location --retry 3 --proto '=https' --tlsv1.2 \ + "https://www.unicode.org/Public/18.0.0/ucd/$name" -o "_inputs/ucd/$name" + done + curl --fail --location --retry 3 --proto '=https' --tlsv1.2 \ + https://www.unicode.org/license.txt -o _inputs/ucd/LICENSE-Unicode.txt + sha256sum _inputs/ucd/* | tee _evidence/unicode-acquisition.sha256 + git rev-parse HEAD > _evidence/stack-tested-merge.txt + printf '%s\n' "$SOURCE_HEAD" > _evidence/stack-head.txt + git -C _inputs/ucns rev-parse HEAD > _evidence/ucns-head.txt + git -C _inputs/oewn rev-parse HEAD > _evidence/corpus-head.txt + # CI source provenance is separate from the artifact verifier's data contract. + cp research/english-gonol/docs/ordinal-work-graph.json _evidence/source-work-graph.json + - name: Verify exact participant graph and unchanged source reader + env: + PYTHONPATH: research/english-gonol + run: | + git fetch --no-tags --depth=1 origin e3eb83764355b4b760a79323cda6a6d75c3a501a + _inputs/ucns/.venv/bin/python - <<'PY' + import ast,hashlib,json,pathlib,subprocess + graph=json.loads(pathlib.Path('research/english-gonol/docs/ordinal-work-graph.json').read_text()) + body={k:graph[k] for k in ('repositories','boundaries')} + assert hashlib.sha256(json.dumps(body,sort_keys=True,separators=(',',':')).encode()).hexdigest()==graph['work_graph_sha256'] + git_roots={ + ('The-Interdependency/ucns','a7bfa0c96daca1f7445ba57c39fa6d8fd62e51eb'):'_inputs/ucns', + ('The-Interdependency/ucns','4f863ad37096b7baab8f62820ad5cb937b62a3a7'):'_inputs/admission-carrier', + ('globalwordnet/english-wordnet','dc343f2683279ecbb13fab4e2fd778d7b162d287'):'_inputs/oewn', + ('The-Interdependency/skill-lib','e74ce1ee8a2cea50b0a6acce7697a6d3d9b2e128'):'_inputs/skill-lib', + } + artifact_roots={ + 'unicode.org/Public/18.0.0/ucd/UnicodeData.txt':'_inputs/ucd/UnicodeData.txt', + 'unicode.org/Public/18.0.0/ucd/DerivedCoreProperties.txt':'_inputs/ucd/DerivedCoreProperties.txt', + } + baseline_key=('The-Interdependency/stack','e3eb83764355b4b760a79323cda6a6d75c3a501a') + expected=set(git_roots) | {(name,None) for name in artifact_roots} | {baseline_key} + seen=set() + for item in graph['repositories']: + key=(item['repository'],item.get('commit')) + assert key in expected and key not in seen, f'unmapped or duplicate participant: {key}' + seen.add(key) + if key in git_roots: + assert subprocess.check_output(['git','-C',git_roots[key],'rev-parse','HEAD'],text=True).strip()==item['commit'] + elif item['repository'] in artifact_roots: + raw=pathlib.Path(artifact_roots[item['repository']]).read_bytes() + assert hashlib.sha256(raw).hexdigest()==item['artifact_sha256'] + elif key==baseline_key: + # The actual consumed files are byte-compared to this commit below. + assert subprocess.check_output(['git','rev-parse',item['commit']+'^{commit}'],text=True).strip()==item['commit'] + else: + raise AssertionError(f'unverified participant: {key}') + assert seen==expected, 'participant graph omits acquired inputs' + active_base=json.loads(pathlib.Path('research/english-gonol/BASE.json').read_text()) + selected=active_base['active_construction']['source_commit'] + assert len(selected)==40 and all(c in '0123456789abcdef' for c in selected) + subprocess.run(['git','fetch','--no-tags','--depth=1','origin',selected],check=True) + for relative in ('english_gonol/ordinal_construct.py','english_gonol/__main__.py', + 'english_gonol/ordinal_definitions.py', + 'english_gonol/language/character_definitions.py', + 'english_gonol/language/data/character_definitions_v1.json', + 'ACTIVE_CONSTRUCTION.json','docs/ordinal-work-graph.json'): + path='research/english-gonol/'+relative + assert pathlib.Path(path).read_bytes()==subprocess.check_output(['git','show',selected+':'+path]),path + base=next(r['commit'] for r in graph['repositories'] if r['repository']=='The-Interdependency/stack') + baseline_path='research/english-gonol/english_gonol/structural_construct.py' + baseline_source=subprocess.check_output(['git','show',base+':'+baseline_path],text=True) + tree=ast.parse(baseline_source) + assignment=next(node for node in tree.body if isinstance(node,(ast.Assign,ast.AnnAssign)) + and ((isinstance(node,ast.Assign) and any(isinstance(t,ast.Name) and t.id=='IMPLEMENTATION_FILES' for t in node.targets)) + or (isinstance(node,ast.AnnAssign) and isinstance(node.target,ast.Name) and node.target.id=='IMPLEMENTATION_FILES'))) + IMPLEMENTATION_FILES=tuple(ast.literal_eval(assignment.value)) + assert IMPLEMENTATION_FILES, 'pinned admission closure must be nonempty' + for name in IMPLEMENTATION_FILES: + path='research/english-gonol/english_gonol/'+name + assert pathlib.Path(path).read_bytes()==subprocess.check_output(['git','show',base+':'+path]),path + PY + - name: Unit and cross-repository geometry tests + env: + PYTHONPATH: research/english-gonol + run: | + _inputs/ucns/.venv/bin/python -m pytest -q \ + _inputs/ucns/tests/test_ordinal_affixiation.py \ + research/english-gonol/tests/test_ordinal_construct.py \ + research/english-gonol/tests/test_ordinal_definitions.py \ + research/english-gonol/tests/test_ordinal_source_inputs.py \ + research/english-gonol/tests/test_active_construction.py \ + | tee _evidence/tests.txt + - name: Build complete source admission then the replacement geometry + env: + PYTHONPATH: research/english-gonol + run: | + /usr/bin/time -v _inputs/ucns/.venv/bin/python -m english_gonol.structural_construct build \ + --source-root _inputs/oewn/src/yaml \ + --ucns-source-root _inputs/admission-carrier \ + --out-dir _admission 2> _evidence/admission-resources.txt + /usr/bin/time -v _inputs/ucns/.venv/bin/python -m english_gonol build \ + _admission _inputs/ucd _constructed \ + 2> _evidence/geometry-resources.txt | tee _evidence/full-verification.json + mkdir -p _constructed/licenses + cp _inputs/oewn/LICENSE.md _constructed/licenses/OEWN-LICENSE.md + cp _inputs/oewn/README.md _constructed/licenses/OEWN-README.md + cp _inputs/ucd/LICENSE-Unicode.txt _constructed/licenses/ + cp _inputs/ucd/ReadMe.txt _constructed/licenses/Unicode-ReadMe.txt + cp _constructed/public-gonol.json _constructed/manifest.json _constructed/verification.json _evidence/ + python - <<'PY' + import json,pathlib + p=json.loads(pathlib.Path('_constructed/public-gonol.json').read_text()) + n=len(p['identities']) + lines=['index\tcodepoint\tscalar_json\tangle_turns'] + lines += [f'{i}\tU+{ord(c):04X}\t{json.dumps(c,ensure_ascii=False)}\t{i}/{n}' for i,c in enumerate(p['identities'])] + pathlib.Path('_evidence/public-gonol.tsv').write_text('\n'.join(lines)+'\n') + print(json.dumps(p['admission_counts'],indent=2)) + PY + - name: Exercise source-exact text query on the completed field + env: + PYTHONPATH: research/english-gonol + run: | + _inputs/ucns/.venv/bin/python -m english_gonol query \ + _constructed 'the geometry' > _evidence/query.json + - name: Retain evidence even after a deterministic failure + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a + with: + name: english-ordinal-evidence + path: _evidence/ + if-no-files-found: error + retention-days: 30 + - name: Retain the actual completed construction, including constitutive source + if: success() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a + with: + name: english-ordinal-construction + path: _constructed/ + if-no-files-found: error + retention-days: 30 diff --git a/STACK_MANIFEST.md b/STACK_MANIFEST.md index d56996f4..4620c364 100644 --- a/STACK_MANIFEST.md +++ b/STACK_MANIFEST.md @@ -17,7 +17,7 @@ Provenance and authority-boundary record for `The-Interdependency/stack`. - English four-view complete-corpus audit UTC: `2026-09-22`, exact view source `1f9a35eb355296fc88d09784c7a3e2e95511ca31`; all 164,864 words, native and independent agreement. - Stack-manifest schema: `the-interdependency.stack-manifest` version `1.1.0` - Work-graph digest (SHA-256 over canonical `repositories` + `research_participants` + `boundaries` JSON): - `f4acf6c40732f486388ed43df896d6e21590f1bf567310a0f45814c56d506268` + `a28ff770ccb5c7cdae017272dd47e3bacb0b7054b07ebe7086fe26d534aaebb6` - Weave workspace creation UTC: `2026-09-28`; new specification-first research starts from the intended multi-arity interleaving mechanism without URPCS inheritance - Pi radix workspace creation UTC: `2026-10-10`; bounded cross-base computation starts from Stack `ccf707f933a9611d7b7fe13a0417378d8b453a14` - Machine-readable copy: [`stack-manifest.json`](stack-manifest.json) @@ -53,6 +53,33 @@ meaning used by that repository. | `The-Interdependency/ptcna` | `97abdd1bbda61a68e0aac8595a32a3cb0ce73487` | main | prime tensor circled neural architecture | canon view `libs/ptcna/`; research `research/ptcna/` | | `The-Interdependency/epac` | `949cb1cb304927942966c9fb396caf6227120e7f` | v0.1.0 | independent implementation and public-contract authority for EPAC | immutable release artifact consumer at integration/epac/; historical forge evidence at research/epac/; libs/epac/ remains unpopulated | +## Selected English ordinal construction + +`english.ordinal/1` replaces the previous completed-geometry default in +`research/english-gonol/`. The exact implementation baseline and its inputs are +research participants; no canonical `libs/` pin is refreshed. The earlier v2 +and four-view records below remain historical evidence, not current geometry. +Use `PYTHONPATH=research/english-gonol python -m english_gonol profile` to inspect +selection, and see [the supported build](research/english-gonol/README.md). + +| Participant ID | Source | Exact identity | Relation | +|---|---|---|---| +| `ordinal-source` | `The-Interdependency/stack` | `2ba7131c5931cccbd779ed08b59f8a2a356729d4` | exact selected ordinal runtime source and supported entrypoint; no independent UCHC release authority | +| `ordinal-geometry` | `The-Interdependency/ucns` | `a7bfa0c96daca1f7445ba57c39fa6d8fd62e51eb` | merged UCNS #241 producer; exact local radius and carrier angle law, not global transport or semantic authority | +| `ordinal-admission` | `The-Interdependency/stack` | `e3eb83764355b4b760a79323cda6a6d75c3a501a` | unchanged thirteen-file source-reader closure; source admission is not the selected completed geometry | +| `ordinal-admission-carrier` | `The-Interdependency/ucns` | `4f863ad37096b7baab8f62820ad5cb937b62a3a7` | inherited scalar identity admission only; these historical angles do not supply the replacement placement | +| `ordinal-corpus` | `globalwordnet/english-wordnet` | `dc343f2683279ecbb13fab4e2fd778d7b162d287` | all exact pinned source records consumed by the ordinal construction | +| `ordinal-unicode-data` | `unicode.org/Public/18.0.0/ucd/UnicodeData.txt` | `0736451de439ae7baf1425136617da495e09ee5afbe6e394374db7009ea08950` | exact immutable Unicode 18.0.0 numeric-category release file consumed by public-gonol admission | +| `ordinal-unicode-math` | `unicode.org/Public/18.0.0/ucd/DerivedCoreProperties.txt` | `09c928886a178fcafd93c29e4bd59073a058e5a100b716d425cb563ab50f68c9` | exact immutable Unicode 18.0.0 Math-property release file consumed by public-gonol admission | +| `ordinal-doctrine` | `The-Interdependency/skill-lib` | `e74ce1ee8a2cea50b0a6acce7697a6d3d9b2e128` | separately pinned ordinal work graph doctrine; root operational snapshot remains unchanged | + +UCNS #241 merged at `6ebbf970ad13a0d2e1c13bd65c496b671e740185`; +the source consumer remains pinned to the tested producer bytes above. The +source-admission closure remains support, not an alternate completed geometry. +[ACTIVE_CONSTRUCTION.json](research/english-gonol/ACTIVE_CONSTRUCTION.json) owns +default selection and [the question record](research/english-gonol/docs/ordinal-questions.md) +separates unanswered construction laws from remaining engineering work. + ## Research-Only Composition Participants These records bind stack-local research inputs without refreshing `libs/`, @@ -221,9 +248,10 @@ release lock and acceptance evidence. EPAC consumes exact UCNS `6eea1828a34ed8ec Stack's direct `libs/ucns/` and separate research UCNS pins remain unchanged. English Gonol Construction remains stack-local research, not EDCM and not an independent -canonical release. Its v2 full pinned-corpus construction/replay survived with compact -committed evidence; that result establishes the construction/replay only, not semantic -quality, geometry canon, or measurement validity. +canonical release. The selected `english.ordinal/1` local construction is distinct +from the historical v2 and four-view receipts. Complete source and ordinal replay +establish construction/recovery, not semantic quality, general geometry canon or +measurement validity. Python Gonol Construction is also stack-local research. Its Python 3.12 source constructor has no independent repository/release authority, and its successful replay @@ -244,7 +272,7 @@ establish a base-independent generating law. ## hmmm - UCNS has no `LICENSE` file at pinned commit `828c0b8`. -- English Gonol Construction remains stack-local research; independent repository/release authority and the exact UCNS displacement law have not been established. +- English Gonol Construction has selected exact ordinal geometry with inherited definitions reconstructed on the expanded base. SPACE references are specified; global transport, vocabulary growth, source-uncovered definitions, downstream migration and independent release still need their own evidence. Native Mobius integration is not required. - Python Gonol Construction remains stack-local research; independent repository/release authority and exact UCNS affixiation geometry remain unresolved. - URPCS is retired for specification divergence; why GPT flattened/substituted the intended construction remains unresolved, and retained evidence is scoped only to the substituted implementation. - Weave now runs one explicit native sequence-cycle profile with corpus normalization and prime-derived splits; cryptographic lifting, authentication/state and full-cipher security remain unresolved. diff --git a/research/english-gonol/ACTIVE_CONSTRUCTION.json b/research/english-gonol/ACTIVE_CONSTRUCTION.json new file mode 100644 index 00000000..2f556a26 --- /dev/null +++ b/research/english-gonol/ACTIVE_CONSTRUCTION.json @@ -0,0 +1,29 @@ +{ + "schema": "english.active-construction/1", + "active_profile": "english.ordinal/1", + "status": "selected-for-new-English-construction", + "entrypoint": "python -m english_gonol", + "implementation": "english_gonol.ordinal_construct", + "geometry_authority": "The-Interdependency/ucns", + "geometry_commit": "a7bfa0c96daca1f7445ba57c39fa6d8fd62e51eb", + "producer_merge": "6ebbf970ad13a0d2e1c13bd65c496b671e740185", + "input_graph": "docs/ordinal-work-graph.json", + "questions": "docs/ordinal-questions.md", + "deprecated_as_completed_geometry": [ + "english_gonol.full_construct_run", + "english_gonol.structural_construct", + "orthogonal-axis or constituent-overlap substitutes" + ], + "retained_support": { + "english_gonol.structural_construct": "source admission and independent source replay only", + "english_gonol.structural_query": "source structure inspection only", + "historical_source_modules": "sealed admission import closure and explicitly historical replay; not supported new-construction routes" + }, + "fallback": "none", + "selection_scope": "local ordinal geometry and recursive recovery, not semantic quality or independent UCHC release", + "hmmm": [ + "Global frame transport and compound definition-origin attachment remain unresolved.", + "Native Mobius lift, operator actions and inference quality are not established.", + "Historical consumers require explicit migration; a merge is not a deployment." + ] +} diff --git a/research/english-gonol/BASE.json b/research/english-gonol/BASE.json index e80065f3..bfe300ef 100644 --- a/research/english-gonol/BASE.json +++ b/research/english-gonol/BASE.json @@ -6,8 +6,8 @@ "source_commit": "7951ca32ba0f2494dc68ff9b7f6a80151918a56d", "ucns_prerequisite": { "repository": "The-Interdependency/ucns", - "commit": "1975fe70cf4e0826a8020c2da3047569e277af64", - "relation": "English Gonol Construction consumes UCNS geometry; UCNS owns geometry and representation" + "commit": "a7bfa0c96daca1f7445ba57c39fa6d8fd62e51eb", + "relation": "selected English ordinal construction consumes exact UCNS geometry; root libs/ucns and the extraction source remain historical pins" }, "canon_path": "../../libs/edcm", "authority": "stack-local research component (extracted from The-Interdependency/edcm)", @@ -24,5 +24,17 @@ "commit": "0873c105799681ea1f7ccb3e619d1f7aebbd85e0", "relation": "exact EDCM semantic metric-origin producer with canonical O/L split; does not refresh the historical extraction source or libs/edcm pin", "authority_transfer": false + }, + "historical_ucns_prerequisite": { + "repository": "The-Interdependency/ucns", + "commit": "1975fe70cf4e0826a8020c2da3047569e277af64", + "relation": "English Gonol Construction consumes UCNS geometry; UCNS owns geometry and representation" + }, + "active_construction": { + "selection": "ACTIVE_CONSTRUCTION.json", + "source_repository": "The-Interdependency/stack", + "source_commit": "2ba7131c5931cccbd779ed08b59f8a2a356729d4", + "work_graph": "../../stack-manifest.json#research_participants[workspace=research/english-gonol/]", + "authority_transfer": false } } diff --git a/research/english-gonol/README.md b/research/english-gonol/README.md index 1acd36c0..c3bcd102 100644 --- a/research/english-gonol/README.md +++ b/research/english-gonol/README.md @@ -1,168 +1,106 @@ # English Gonol Construction -## Structural profile v1 — new construction entrypoint +## Active construction: ordinal affixiation -The operator-ratified [structural profile](docs/structural-profile-v1.md) replaces -non-whitespace runs for new structural work. It builds the complete pinned -corpus, keeps punctuation outside word identity, preserves ordered shared -participations, and exposes read-only query/replay through -`english_gonol.structural_query.StructuralField`. Start with the commands in -that document and the [exact input graph](docs/structural-profile-work-graph.json). -The v2 results below remain historical and are not receipts for the new profile. -Stack owns the implementation; UCHC carries the extracted candidate. Geometry, -compound lexical origins and graduation retain their explicit hmmm boundaries. +`english.ordinal/1` is the selected replacement for new English construction. +[ACTIVE_CONSTRUCTION.json](ACTIVE_CONSTRUCTION.json) owns that selection. +Distance follows repeated ordinal halving; angle comes from the expanded public +gonol. Closed interiors, order, multiplicity, whitespace and source provenance +remain recoverable. There is no fallback to previous geometry. - -Stack-local research component for English lexical/gonol construction. - -## Authority - -```text -METAPAT -> affixiation semantics -UCNS -> gonol / Möbius / Public Gonol geometry -English -> text admission and linguistic/semantic construction -EDCM -> measurement/evaluation only -``` - -English construction consumes UCNS geometry; it does not invent geometry or -move English semantics into UCNS. - -## Historical v2 invariants - -```text -every admitted character is a gonol -one exact scalar -> one shared character identity -one exact surface -> one shared word identity - -glyph axes - -> ordered glyph-axis tensor - -> closed word - -> word axis +```bash +export PYTHONPATH=research/english-gonol +python -m english_gonol profile +python -m english_gonol build /path/to/admission /path/to/ucd /path/to/new-output +python -m english_gonol verify /path/to/new-output +python -m english_gonol query /path/to/new-output 'the geometry' ``` -Once closed, a gonol is atomic at an admissible consuming scale. Reuse preserves -identity while order, multiplicity, relation, source position, and provenance -remain recoverable. - -The Stack-forged Hilbert inference candidate treats those already-declared axes -as mathematical basis directions rather than inserting Cartesian coordinates. -A word's ordered glyph-axis construction is preserved when the closed word -becomes its own higher-scale axis. Exact construct identity travels with every -basis axis. See [Hilbert inference candidate](docs/hilbert-inference.md). - -This mathematical use of vector is domain-qualified as `HilbertStateVector`. -It is not METAPAT `Vector` and not a UCNS displacement vector. The canonical -scalar field, cross-origin inner products, amplitudes/phases and inference -operators remain unresolved. +Use the exact UCNS and source identities, dependency setup and admission commands +in [ordinal construction](docs/ordinal-construction.md). The **English ordinal +affixiation** GitHub workflow can also be run manually from Actions. It builds +and independently replays the complete pinned corpus through the supported +entrypoint. An existing output directory is never overwritten. -Corpus occurrences, sense ids, and synset ids are evidence/provenance, not -independent gonols. Definitions are word-anchored constructions: each retains -the shared word origin, exact ordered constituent word/whitespace-character -identities, source ordinal, direct binding, and chain predecessor. +The prior full construction recorded 4,698 public scalar positions, 103,053 closed +words, 420,006 frames and 4,792,299 placements. A changed implementation requires +a new exact-source build and replay; prior receipts do not certify current bytes. Source and geometry receipts belong +to `english.ordinal/1`; old counts and receipts are not interchangeable with it. +These counts establish constructed scope, not semantic or inference quality. -## Full construct v2 — sealed historical profile +## Authority -`english_gonol.full_construct_run` builds the complete pinned OEWN 2025 corpus -without promoting bookkeeping into geometry. +UCNS owns geometric objects and the ordinal operation. Stack owns this English +construction and its source/admission profile. METAPAT owns affixiation semantics; +EDCM may measure completed outputs but does not define them. Selecting this +profile does not graduate UCHC or refresh the root `libs/` snapshots. The exact +work graph in `docs/ordinal-work-graph.json` binds the consumed geometry separately. -```text -word origin -> D1 -> D2 -> ... -> Dn # chain topology -word origin -> D1, D2, ... Dn # direct topology +## Identity and scale -ordinal evidence -+ semantic evidence -+ exact sentence-context evidence --> preponderance relative to the rest of the sentence in which the word appears --> hmmm until UCNS derives the displacement geometry -``` +In this text-domain construction, every admitted character is a gonol: +one exact scalar -> one shared character identity; one exact surface -> one shared word identity. +Once closed, a gonol is atomic at an admissible consuming scale, while its ordered +interior remains recoverable. This does not impose a mandatory adjacent-scale ladder. -Definitions preserve their source exactly: every maximal non-whitespace run -reuses one shared word identity and every whitespace scalar reuses one shared -character identity. No normalization is applied. +Authority routing remains METAPAT -> affixiation semantics; UCNS -> exact geometry; +English Gonol Construction -> text-domain participants; EDCM -> measurement/evaluation only. +Construction/recovery does not validate EDCM measurement. See +[the language boundary](docs/GONOL_LANGUAGE_BOUNDARY.md). -The builder does not create sentence/sense/synset/n-gram singleton objects, -occurrence-object ledgers, closure graphs, synthetic relation circles, -tangencies, attention frames, or duplicate JSON copies of the database. It does -not synthesize weights, displacement vectors, Cartesian coordinates, centers, -radii, motion, or tangency. The separate Hilbert candidate constructs -mathematical state vectors from axes already declared by this construction; it -does not supply a displacement law. +## Replaced and retained -### Verified full-corpus result +The structural-only build is **deprecated as a completed geometric construction**. +`structural_construct` remains the verified source-admission/replay stage consumed +by the replacement, not a competing geometry. `StructuralField` inspects that +source stage only. Its 13-file sealed import closure remains unchanged so existing +source evidence remains independently replayable. -Evidence is committed in `experiments/full-construct-v2/`. +Full construct v2, orthogonal-axis placement and constituent-overlap views are +**historical, not supported routes for new construction**. Their old build commands +are removed from this entrypoint and usage path. Retained source modules support +sealed admission and explicitly historical replay; retention grants no current +geometry authority. The supported CLI rejects retired profile commands and does +not silently return an old artifact when the ordinal constructor is unavailable. -- 118 shared character identities -- 164,864 shared word identities -- 1,739,949 ordered word-character references -- 185,155 word-anchored definitions -- 3,535,375 exact ordered definition components -- 866,183 resolved semantic evidence rows -- 0 unresolved semantic evidence rows -- SQLite integrity: `ok` -- logical receipt: `12277b4959c0c72b7af12097b8a77bf91866bbf669e7f4ac07b6a5f1426ebb57` -- generated database: 306,331,648 bytes -- build wall time: 1:34.79 -- peak RSS: 286,692 KiB +The selected build reconstructs the inherited definition layer over the expanded +carrier in required `character-definitions.json`, bound by its manifest digest. +Independent replay checks every entry, relation, placement and closed interior; +verified entries reuse the previous final-y behavior. The historical constructor +is never used for these closures. [Answer 4](docs/ordinal-questions.md) owns the +source bindings, preservation tests, complete-carrier witness and remaining unknowns. -The database itself remains generated state; the compact manifest, integrity -receipt, and resource receipt are committed. +Historical documentation and evidence: -## Run historical v2 +- [Full construct v2](docs/full-construct-v2.md) and `experiments/full-construct-v2/`. +- [Four-view audit](docs/hyperspace-views-full-v1.md) and `FULL_VIEW_AUDIT.json`. +- [Hilbert candidate](docs/hilbert-inference.md), a separately declared historical + representation, not the new relational geometry or evidence of inference quality. -```bash -python -m english_gonol.full_construct_run \ - --source-root /path/to/oewn-2025/src/yaml \ - --ucns-source-root /path/to/ucns \ - --out-dir experiments/full-construct-v2 -``` +The previous active profile's [segmentation contract](docs/structural-profile-v1.md) +remains the source-admission contract. Punctuation is not silently folded into word +identity; raw source, shared words, exact scalars and ordered participation survive. +Any future source-rule alteration needs its own profile and complete replay. -Tests: +## Tests and migration ```bash -python -m pytest -q tests +python -m pytest -q research/english-gonol/tests ``` -See [`docs/full-construct-v2.md`](docs/full-construct-v2.md). - -## Four-view full-corpus audit - -The [complete view report](docs/hyperspace-views-full-v1.md) covers all **164,864 words**, -all fifteen nonempty view combinations, and three complete address-renumbering controls. -Native and independent evidence agree exactly, including per-word native reconstruction. -The synthesis distinguishes 137,013 values. Removing view 1, 2, or 3 loses 10,267, -20,042, or 125,813 distinctions respectively; removing view 4 loses none. +Use the UCNS dependency specified by the work graph. The new default's tests cover +ordinal-only routing, retired-command rejection, selection mismatch and no-compute +profile/help. Geometry tests and full-corpus replay remain separate witnesses. -View 1 is nullable constituent-overlap metadata, view 2 is the terminal phase/frame, -view 3 carries coarse address/provenance information, and view 4 is a derivable witness. -Their inference usefulness requires a task beyond distinctness. The frozen protocol is -[`FULL_VIEW_AUDIT.json`](FULL_VIEW_AUDIT.json); complete receipts are in -[`experiments/hyperspace-views-full-v1/`](experiments/hyperspace-views-full-v1/). -The report contains the exact source/database bindings and full replay commands. - -## Historical research - -The orthogonal-carrier sweep and earlier definition-affixiation run remain -separate experiments and supply no placement law to v2. The falsified -`full_singleton_run.py` architecture and its UCNS singleton-axis support were -deleted rather than retained as an active precedent. - -## Migration standing - -The current lifecycle record is [`MIGRATION.json`](MIGRATION.json). UCHC is -extracted and has a clean-installed immutable candidate verified in Stack, but -stable publication, released-artifact reconsumption, severance of the local -implementation path and a scoped authority-transition receipt remain incomplete. -Stack therefore remains the implementation forge for this new candidate. +[MIGRATION.json](MIGRATION.json) retains the UCHC extraction/release standing. +Older pinned consumers are not silently upgraded by this source merge. They must +consume the new profile explicitly and replay their own contracts; historical +metrics are not renamed as ordinal measurements. ## hmmm -The exact UCNS law mapping ordinal + semantic + sentence-context evidence to -geometric displacement remains unresolved. No English-layer rule may fill that -boundary with invented weights, directions, distances, displacement vectors, -Cartesian coordinates, centers, radii, tangencies, or motion. - -For the Hilbert candidate, the canonical scalar field (`R` or `C`), -cross-origin inner products/angles, amplitudes/phases, learned inference -operators, sense selection, sentence-axis promotion and completed UCHC -graduation remain unresolved. +[Answers and remaining work](docs/ordinal-questions.md) distinguish the implemented +inherited definitions from current-head full-corpus and downstream evidence. +SPACE is the specified attachment reference; global transport still needs its +witness. Native Mobius integration is not required. Vocabulary growth is the +remaining construction question; source-uncovered definitions remain explicit. diff --git a/research/english-gonol/docs/ordinal-construction.md b/research/english-gonol/docs/ordinal-construction.md new file mode 100644 index 00000000..3df61c0c --- /dev/null +++ b/research/english-gonol/docs/ordinal-construction.md @@ -0,0 +1,132 @@ +# English ordinal construction — replacement profile + +Operator request: radial distance is repeated halving by occurrence ordinal; +angle is defined by the newly expanded public gonol. This selected replacement profile constructs +that law instead of reinterpreting the previous orthogonal or overlap metrics. + +## Public gonol + +Admission is the exact union of all scalars admitted by the complete pinned +OEWN structural field, all inherited Public Gonol scalars, Unicode 18.0.0 +numeric categories Nd/Nl/No, its derived Math property, and the explicit logical +supplement in `ordinal_construct.py`. No normalization, case folding, surrogate +positions, prime padding, missing-scalar fallback, or inferred operator meaning. + +The new layout keeps SPACE first and the inherited 157 anchors in their +relative cyclic order. Additional scalars, sorted by code point, are distributed +as evenly as possible after those anchors. Every resulting position i has the +exact angle i/N turns. The actual N comes from the union, not an estimated count. +This balanced insertion is an explicit engineering profile choice. It is not +claimed to recover a hidden algorithm behind the old fixed arrangement. + +Closed words use a second explicit carrier ordered lexicographically by their +sequences of new public-gonol indexes. That ordering is likewise a declared +profile choice, not a semantic ranking or a last-glyph angle. The UCNS constructor +accepts any explicitly declared carrier for further recursive scales. + +## Geometry and recovery + +For zero-based occurrence i among n participants, radius = R/2**(n-i). +The newest participant is at R/2; an append halves previous radii without +changing their carrier angles. Each closed word retains its full ordered glyph +interior. A higher construction contains references to those actual closed +interiors, with repeated occurrences, whitespace, punctuation and original +source relations intact. + +The initial equidistant field and the angular placement are distinct stages. +Arbitrary circular placement is not an isometry of a high-dimensional simplex. +SPACE's scalar angle is zero; the local frame center is the radial reference. +A SPACE occurrence is not silently placed at radius zero or discarded. + +## Inherited definitions + +[Answer 4](ordinal-questions.md) specifies reuse of the prior definition method +and applicable functions, reconstructed over the new base. Preserve meaning, +source associations and ordered interiors; replace the old carrier/geometry +bindings. The source audit and required preservation/replay witnesses live in +that answer. This instruction is settled; reconstruction verification remains +engineering work, not a request for new operator semantics. + +## Artifact + +`build` creates a new directory only: + +- `admission/`: exact verified corpus, raw source evidence, ordered words, + components, definition origins and provenance. This is constitutive material, + not an optional cache. Its old carrier positions are historical admission + metadata and never supply the replacement's angles. +- `public-gonol.json`, `word-carrier.json`: complete exact angular registries. +- `geometry.db`: every closed word/source frame and its ordinal placements, + linked to the retained interiors and source records. +- `ucd/`: the exact property input bytes and manifest-bound hashes. +- `manifest.json`, `verification.json`: new profile identity and independent + all-placement replay; neither is a substitute for the construction itself. + +The hosted workflow also retains source licenses, an exact angular TSV, query +output and resource receipts. It writes no repository files and does not merge. +Old structural receipts are never presented as receipts for this geometry. +The source work graph is CI provenance, retained separately as +`source-work-graph.json` in the evidence archive and bound to the selected source +commit by CI. It is not packaged as construction data or covered by the artifact +verifier's `SURVIVED` result. The construction verifier covers its declared +manifest, carrier registries, Unicode inputs, source admission and geometry; +auxiliary licensing text is not a geometry or authority claim. + +## Execute + +Use the exact UCNS commit in `ordinal-work-graph.json`. Install its locked test +extra and PyYAML 6.0.2. The existing structural parser remains separately pinned; +expanding the public scalar registry does not silently change its segmentation. + +```bash +export PYTHONPATH=research/english-gonol +python -m english_gonol.structural_construct build \ + --source-root /path/to/pinned-oewn/src/yaml \ + --ucns-source-root /path/to/pinned-admission-carrier \ + --out-dir /path/to/new-admission +python -m english_gonol build \ + /path/to/new-admission /path/to/unicode-18-ucd /path/to/new-geometry +python -m english_gonol verify /path/to/new-geometry +python -m english_gonol query /path/to/new-geometry 'the geometry' +``` + +Full corpus means the source verifier has replayed the exact 73-file OEWN tree +and every source record. The new independent checker additionally recomputes all +local exponents, angles, references and closure receipts without calling the +producer's placement function. Missing, added or altered rows fail. A fixture +Unicode file or unit-test success alone is not complete-corpus evidence. + +## File plan and resources + +`english_gonol/ordinal_construct.py` owns admission expansion, composition and +artifact verification; UCNS owns the geometric law. `tests/test_ordinal_construct.py` +owns fixture/property regressions. The workflow owns explicit input acquisition +and full replay. `ordinal-work-graph.json` binds input ownership and exact source +states. The supported entrypoint and retirement are declared in `../ACTIVE_CONSTRUCTION.json`. +The root research-participant manifest and English BASE explicitly record the +selected source and inputs. Canonical release pins, UCHC graduation and the website +remain unchanged. The construction source commit is recorded by hosted evidence, avoiding a +self-referential committed head hash. + +Expected working footprint is below 4 GiB; the hosted runner checks free disk +before work. The source has 46,372,711 raw YAML bytes. No artificial science +stopping time is introduced; the workflow uses the platform's job boundary. +The local environment cannot fetch the complete public source/UCD bytes, so +full construction is executed by the repository workflow rather than calling a +small local fixture the full construct. + +## hmmm + +The layout choices are explicit and replaceable only through a new profile and +full rebuild. The [answers and remaining work](ordinal-questions.md) record the +specified SPACE attachments, excluded native Mobius integration, inherited +definition method, remaining admission question and engineering witnesses. +Local placement does not establish attachment transport, inherited-definition +reconstruction, operator behavior, inference quality or UCHC release. +Earlier complete-corpus receipts establish only their recorded implementation. +Every implementation change requires its own hosted construction and replay; +inspect the exact run, tested merge tree and generated manifest before assigning +that standing. CI compares the selected source pin with the actual module bytes. +Unicode inputs are regular nonsymlink files, read within the acquisition budget +and matched to both pinned complete-release hashes before parsing or copying. +See `ordinal-questions.md` for unresolved construction laws and migration work. diff --git a/research/english-gonol/docs/ordinal-questions.md b/research/english-gonol/docs/ordinal-questions.md new file mode 100644 index 00000000..928fe17a --- /dev/null +++ b/research/english-gonol/docs/ordinal-questions.md @@ -0,0 +1,95 @@ +# Ordinal construction: answers and remaining work + +`english.ordinal/1` is the selected construction. Inspect it with +`PYTHONPATH=research/english-gonol python -m english_gonol profile`. +Run `verify OUTPUT` before consuming an artifact. These boundaries do not reopen +the specified construction or turn engineering work into operator questions. + +## Construction law + +For zero-based occurrence i among n participants, radius is R/2**(n-i); the newest +is at R/2. Angle is the participant's exact index/arity on its declared carrier. +Closed interiors remain recoverable through recursive atomic participation. +The initial equidistant field and subsequent circular placement are distinct; +the latter is not claimed to preserve every initial pairwise distance. + +This profile uses balanced insertion around inherited scalar anchors and orders +its word carrier by public-gonol-index sequences. These are explicit versioned +layout choices, not discovered semantic laws. Changing them requires rebuilding. + +## Operator answers + +### 1. Nested attachment: SPACE + +Operator answer: `1. space`. SPACE supplies the attachment reference between a +closed construction and its consuming frame. Do not ask for this choice again. +`Factory.word`, `Factory.record` and independent frame replay preserve the SPACE +origin. Stored origins and closure links do not alone establish evaluated global +transport. Implementing and testing that transport remains engineering work, +without substituting an implicit rotation, translation or scale law. + +### 2. Compound definition attachment: SPACE + +Operator answer: `2. space`. A multiword lexical entry's definition attaches at +SPACE, not an arbitrarily chosen constituent word. Preserve exact entry ownership, +constituent order and definition succession. A source-bound attachment witness is +still required; the reference itself is settled. + +### 3. Native Mobius integration: not required + +Operator answer: `3. not required`. Native lifting, sheet-state transport and +360/720-degree return tests are outside this profile's requirements, not open +questions or completion/merge prerequisites. This does not retire UCNS native +Mobius work or remove the SPACE attachment obligations. + +### 4. Prior definitions and applicable functions: reconstructed on the new base + +Operator answer: `define them the same way the previous iteration did. most of +that part of the previous implementation should still apply, just requires +iteration over the new base.` + +`../english_gonol/language/character_definitions.py` and its +`data/character_definitions_v1.json` remain the unchanged definition/behavior +sources. The replacement `../english_gonol/ordinal_definitions.py` iterates the +expanded carrier, retaining every inherited entry, overlapping role, pronunciation, +orthographic rule, number-name link, numerology entry and explicit unknown. +It calls UCNS ordinal affixiation, never the historical constructor. Verified +entries reuse the prior final-y behavior rather than duplicate its law. + +The supported `build` now requires `character-definitions.json`. Its digest is +mandatory in the geometry manifest; `verify` replays every definition placement, +ordered interior, source binding and carrier index by an independent formula. +Missing, extra, reordered or altered data fail. Existing artifacts must be rebuilt; +old receipts are not rebranded as evidence for the changed implementation. + +The committed `../experiments/ordinal-definitions/migration-witness.json` records +reconstruction over all 4,698 scalars from the supplied complete field: 4,757 +definition closures and ten number-name interiors matching existing corpus word +receipts. The inherited source leaves 4,643 scalar definitions explicitly unknown. +This is full definition-layer coverage, not a new complete-corpus admission replay +or current-head hosted CI. `../tests/test_ordinal_definitions.py` supplies +preservation, no-old-constructor, independent-replay and corruption tests. + +### 5. Vocabulary growth: open + +Current behavior is an exact pinned repertoire. Queries reject unknown words and +unadmitted scalars, without normalization or dynamic carrier mutation. Changing +carrier order or arity requires a new profile and rebuilding affected placements. +An open-text admission policy must preserve old-profile replay and avoid silent +reinterpretation; neither last-glyph angles nor invented hash angles substitute +for that policy. This question does not block the existing pinned construction. + +## Engineering, not further questions for the operator + +SPACE transport/compound-attachment verification; separating the sealed admission +helper closure before physically removing retired modules; exact downstream +UCHC/EDCM/ZFAE/a0 migration; durable artifact publication; and utility testing +against a predeclared downstream comparison remain distinct work. Source selection +is neither independent UCHC release nor semantic or measurement validation. + +## hmmm + +Only question 5 remains a construction choice. The inherited definition migration +is executable and locally tested; current-head full-corpus build/replay and review +must establish their own evidence. SPACE reference choices are settled while their +global transport witnesses remain outstanding. Native Mobius integration is excluded. diff --git a/research/english-gonol/docs/ordinal-work-graph.json b/research/english-gonol/docs/ordinal-work-graph.json new file mode 100644 index 00000000..5521ec3a --- /dev/null +++ b/research/english-gonol/docs/ordinal-work-graph.json @@ -0,0 +1,61 @@ +{ + "schema": "the-interdependency.stack-manifest", + "version": "1.0.0", + "repositories": [ + { + "repository": "The-Interdependency/ucns", + "commit": "a7bfa0c96daca1f7445ba57c39fa6d8fd62e51eb", + "authority": "generic exact ordinal geometry", + "relation": "new producer; no English semantic authority" + }, + { + "repository": "The-Interdependency/ucns", + "commit": "4f863ad37096b7baab8f62820ad5cb937b62a3a7", + "authority": "historical scalar-admission carrier", + "relation": "retained source-admission carrier only; historical angles are not consumed by the replacement constructor" + }, + { + "repository": "The-Interdependency/stack", + "commit": "e3eb83764355b4b760a79323cda6a6d75c3a501a", + "authority": "English source admission and construction forge", + "relation": "unchanged structural-source baseline; candidate source bound by generated implementation digest and observed head" + }, + { + "repository": "globalwordnet/english-wordnet", + "commit": "dc343f2683279ecbb13fab4e2fd778d7b162d287", + "authority": "complete English corpus evidence", + "relation": "all exact pinned source records" + }, + { + "repository": "unicode.org/Public/18.0.0/ucd/UnicodeData.txt", + "artifact_sha256": "0736451de439ae7baf1425136617da495e09ee5afbe6e394374db7009ea08950", + "authority": "Unicode 18.0.0 numeric-category source", + "relation": "immutable release file identity consumed by public-gonol admission" + }, + { + "repository": "unicode.org/Public/18.0.0/ucd/DerivedCoreProperties.txt", + "artifact_sha256": "09c928886a178fcafd93c29e4bd59073a058e5a100b716d425cb563ab50f68c9", + "authority": "Unicode 18.0.0 Math-property source", + "relation": "immutable release file identity consumed by public-gonol admission" + }, + { + "repository": "The-Interdependency/skill-lib", + "commit": "e74ce1ee8a2cea50b0a6acce7697a6d3d9b2e128", + "authority": "build discipline", + "relation": "current loaded governing contracts" + } + ], + "boundaries": { + "authority_transfer": false, + "proof_status_transfer": false, + "measurement_status_transfer": false, + "semantic_mapping": "English supplies exact participants and explicit ordering policy; UCNS supplies geometry only", + "agent_scope": "cross-repository-work-graph", + "hmmm": [ + "Native Mobius lift and global frame attachment remain unresolved.", + "Balanced scalar insertion and composite angular ordering are declared profile choices.", + "The historical admission carrier is pinned separately in preserved source metadata; its angles are not consumed by the new constructor." + ] + }, + "work_graph_sha256": "5d92bd50ce35e27ea81b815f3b90cead901fcf6eb275c56ec3e451ce7ba3d219" +} diff --git a/research/english-gonol/english_gonol/__main__.py b/research/english-gonol/english_gonol/__main__.py new file mode 100644 index 00000000..de6175db --- /dev/null +++ b/research/english-gonol/english_gonol/__main__.py @@ -0,0 +1,88 @@ +"""Supported English construction entry point; retired profiles have no fallback. + +Usage: python -m english_gonol profile + python -m english_gonol build ADMISSION UCD NEW_OUTPUT + python -m english_gonol verify OUTPUT + python -m english_gonol query OUTPUT 'the geometry' + +Build consumes verified source admission; admission alone is not completed +geometry. Writes and full-corpus replay remain owned by ordinal_construct. +""" +from __future__ import annotations + +import argparse +import json +from pathlib import Path + +# === MODULE_BUILD === +# id: english_active_construction_entrypoint +# module_name: __main__ +# module_kind: adapter +# summary: selects the ordinal constructor without restoring deprecated build routes +# owner: Erin Spencer +# public_surface: main +# internal_surface: active_profile +# tests: tests/test_active_construction.py +# auth_boundary: none +# storage_boundary: write +# network_boundary: none +# user_data_boundary: read +# admin_only: false +# rollout: default package entrypoint selected by ACTIVE_CONSTRUCTION.json +# rollback: revert selection explicitly; never silently substitute historical geometry +# unresolved: historical consumer migration is separate from this source default +# === END MODULE_BUILD === +# === CONTRACTS === +# id: english_active_no_legacy_fallback +# given: a build, verification or query through the supported package entrypoint +# then: only the selected ordinal implementation runs; retired or inconsistent profiles fail closed +# id: english_profile_without_compute +# given: a profile or help request +# then: show selection or usage without loading geometry or replaying the corpus +# === END CONTRACTS === + + +def active_profile() -> dict: + """Read the source-owned selection, independent of installed geometry packages.""" + path = Path(__file__).resolve().parents[1] / 'ACTIVE_CONSTRUCTION.json' + profile = json.loads(path.read_text(encoding='utf-8')) + if (profile.get('schema') != 'english.active-construction/1' + or profile.get('implementation') != 'english_gonol.ordinal_construct' + or profile.get('fallback') != 'none'): + raise ValueError('invalid active construction selection; no legacy fallback') + return profile + + +def main(argv: list[str] | None = None) -> None: + """Run only the current construction; historical commands are not selectable.""" + parser = argparse.ArgumentParser(description=__doc__) + commands = parser.add_subparsers(dest='command', required=True) + commands.add_parser('profile', help='show selection and unresolved boundaries; no compute') + build = commands.add_parser('build', help='complete ordinal geometry from source admission') + for name in ('admission', 'ucd', 'output'): + build.add_argument(name, type=Path) + verify = commands.add_parser('verify', help='independently replay the complete construction') + verify.add_argument('output', type=Path) + query = commands.add_parser('query', help='verify then construct exact admitted text') + query.add_argument('output', type=Path) + query.add_argument('text') + args = parser.parse_args(argv) + profile = active_profile() + if args.command == 'profile': + value = profile + else: + from english_gonol import ordinal_construct as constructor + if (profile['active_profile'] != constructor.PROFILE + or profile['geometry_commit'] != constructor.UCNS_COMMIT): + raise ValueError('active selection differs from constructor; no legacy fallback') + if args.command == 'build': + value = constructor.build(args.admission, args.ucd, args.output) + elif args.command == 'verify': + value = constructor.verify(args.output) + else: + value = constructor.query(args.output, args.text) + print(json.dumps(value, ensure_ascii=False, indent=2)) + + +if __name__ == '__main__': + main() diff --git a/research/english-gonol/english_gonol/ordinal_construct.py b/research/english-gonol/english_gonol/ordinal_construct.py new file mode 100644 index 00000000..58069e12 --- /dev/null +++ b/research/english-gonol/english_gonol/ordinal_construct.py @@ -0,0 +1,627 @@ +"""Build the replacement English angular/radial construction. + +Usage (from research/english-gonol, with UCNS on PYTHONPATH): + python -m english_gonol.ordinal_construct build ADMISSION UCD NEW_OUTPUT + python -m english_gonol.ordinal_construct verify OUTPUT + python -m english_gonol.ordinal_construct query OUTPUT 'the geometry' + +ADMISSION is a complete, verified structural-profile field, used for source +admission and relations only. The new construction never consumes its old +public_position column. UCD contains UnicodeData.txt and DerivedCoreProperties.txt +from the explicitly declared Unicode release. No network occurs in this module. +""" +from __future__ import annotations + +import argparse +from contextlib import closing +from functools import lru_cache +from hashlib import sha256 +from itertools import groupby +import json +import os +from pathlib import Path +import shutil +import sqlite3 +import stat +import tempfile + +from ucns.ordinal_affixiation import Carrier, Participant, affixiate + +# === MODULE_BUILD === +# id: english_ordinal_construction +# module_name: ordinal_construct +# module_kind: engine +# summary: expanded corpus/Unicode public gonol and exact recursive ordinal geometry +# owner: Erin Spencer +# public_surface: repertoire, Factory, build, verify, query, main +# internal_surface: canonical artifact rows and strict corpus admission +# auth_boundary: read-only exact complete structural source +# storage_boundary: new caller-selected output; no overwrite +# network_boundary: none +# user_data_boundary: query text in memory unless explicitly saved by caller +# admin_only: false +# tests: tests/test_ordinal_construct.py +# rollout: explicit replacement profile english.ordinal/1; legacy receipts remain historical +# rollback: remove candidate import/output; no silent profile downgrade +# unresolved: global SPACE-frame transport, source-uncovered definitions, semantic interpretation +# === END MODULE_BUILD === +# === CONTRACTS === +# id: english_expanded_repertoire +# given: complete corpus scalars, exact inherited carrier, pinned Unicode properties +# then: every required scalar is admitted once; registry is reproducible +# id: english_recursive_ordinal_geometry +# given: closed words and exact source components +# then: UCNS places by ordinal radius and declared carrier angle while interiors remain recoverable +# id: english_complete_geometry_replay +# given: a complete replacement artifact +# then: independently check every placement, source reference, closure receipt and raw source input +# === END CONTRACTS === + +PROFILE = 'english.ordinal/1' +UNICODE_VERSION = '18.0.0' +UCD_FILES = ('UnicodeData.txt', 'DerivedCoreProperties.txt') +UCD_MAX_BYTES = 10_000_000 +JSON_MAX_BYTES = 64_000_000 +# Exact complete Unicode 18.0.0 release bytes, retained in the full-corpus artifact. +# Admission pins content, not a header, local registry, or minimal ASCII subset. +UCD_RELEASE_SHA256 = { + 'UnicodeData.txt': '0736451de439ae7baf1425136617da495e09ee5afbe6e394374db7009ea08950', + 'DerivedCoreProperties.txt': '09c928886a178fcafd93c29e4bd59073a058e5a100b716d425cb563ab50f68c9', +} +MANIFEST_BOUNDARIES = ( + 'Local frame placement is constructed; global origin attachment is not inferred.', + 'Layout recipes are explicit profile choices, not discovered semantic laws.', + 'Native Mobius lift is not required; inference quality remains untested.', +) +MANIFEST_KEYS = frozenset(( + 'schema', 'source_receipt', 'source', 'ucns_commit', 'implementation_sha256', + 'public_sha256', 'glyph_carrier_receipt', 'word_carrier_receipt', 'counts', + 'geometry_receipt', 'character_definitions_receipt', 'hmmm', +)) +LOGIC = tuple('¬∧∨⊻⊼⊽→←↔⇒⇐⇔∀∃∄⊢⊣⊤⊥⊨⊭⊬⊮⊯≡≠=<>≤≥') +LAYOUT = 'legacy-anchor-balanced-insertion/1' +WORD_LAYOUT = 'lexicographic-public-gonol-index-sequences/1' +UCNS_COMMIT = 'a7bfa0c96daca1f7445ba57c39fa6d8fd62e51eb' +UCNS_MODULE_SHA256 = '784cf61d185d2a54259c0e013af8c0ea2b6d79fad32beaffda7819f0c0e512ad' +SQL = ''' +PRAGMA foreign_keys=ON; +PRAGMA journal_mode=DELETE; +CREATE TABLE frames(kind TEXT NOT NULL, id INTEGER NOT NULL, identity TEXT NOT NULL, + origin TEXT NOT NULL, relation TEXT NOT NULL, source TEXT NOT NULL, receipt TEXT NOT NULL, + length INTEGER NOT NULL CHECK(length>0), PRIMARY KEY(kind,id)) WITHOUT ROWID; +CREATE TABLE placements(kind TEXT NOT NULL, id INTEGER NOT NULL, ordinal INTEGER NOT NULL, + participant_kind TEXT NOT NULL CHECK(participant_kind IN ('word','character')), + participant_id INTEGER NOT NULL, carrier_index INTEGER NOT NULL, + exponent INTEGER NOT NULL CHECK(exponent>0), interior_receipt TEXT, + PRIMARY KEY(kind,id,ordinal), FOREIGN KEY(kind,id) REFERENCES frames(kind,id)) WITHOUT ROWID; +''' + + +def canonical(value: object) -> bytes: + return json.dumps(value, ensure_ascii=False, sort_keys=True, + separators=(',', ':'), allow_nan=False).encode('utf-8') + + +def digest(value: object) -> str: + return sha256(canonical(value)).hexdigest() + + +def file_hash(path: Path) -> str: + with path.open('rb') as stream: + return sha256(stream.read()).hexdigest() + + +def _producer_check() -> None: + import ucns.ordinal_affixiation as geometry + if file_hash(Path(geometry.__file__)) != UCNS_MODULE_SHA256: + raise ValueError('UCNS geometry source differs from the exact pinned producer') + + +def scalar(value: str) -> str: + if (not isinstance(value, str) or len(value) != 1 + or 0xD800 <= ord(value) <= 0xDFFF): + raise ValueError('one exact Unicode scalar required') + return value + + +def _range(value: str) -> range: + bounds = value.strip().split('..') + first, last = int(bounds[0], 16), int(bounds[-1], 16) + if len(bounds) > 2 or not 0 <= first <= last <= 0x10FFFF: + raise ValueError('invalid Unicode property range') + return range(first, last + 1) + + +def _parse_unicode_properties(blobs: dict[str, bytes], version: str): + """Parse supplied property syntax; this helper establishes no release identity.""" + numbers: set[str] = set() + maths: set[str] = set() + pending = None + if any(len(raw) > UCD_MAX_BYTES for raw in blobs.values()): + raise ValueError('Unicode input exceeds per-file acquisition budget') + data = blobs[UCD_FILES[0]].decode('utf-8') + for line in data.splitlines(): + if not line.strip(): + continue + fields = line.split(';') + if len(fields) != 15: + raise ValueError('invalid UnicodeData record') + cp, name, category = int(fields[0], 16), fields[1], fields[2] + if not 0 <= cp <= 0x10FFFF: + raise ValueError('UnicodeData code point out of range') + if name.endswith(', First>'): + if pending is not None: + raise ValueError('nested UnicodeData range') + pending = (cp, category) + continue + if name.endswith(', Last>'): + if pending is None or pending[1] != category or cp < pending[0]: + raise ValueError('unmatched UnicodeData range') + points = range(pending[0], cp + 1) + pending = None + else: + if pending is not None: + raise ValueError('unterminated UnicodeData range') + points = (cp,) + if category in ('Nd', 'Nl', 'No'): + numbers.update(scalar(chr(p)) for p in points) + if pending is not None: + raise ValueError('unterminated UnicodeData range') + derived = blobs[UCD_FILES[1]].decode('utf-8') + if f'DerivedCoreProperties-{version}.txt' not in derived[:4096]: + raise ValueError('Unicode release header mismatch') + for line in derived.splitlines(): + body = line.split('#', 1)[0].strip() + if not body: + continue + fields = [field.strip() for field in body.split(';')] + if len(fields) >= 2 and fields[1] == 'Math': + maths.update(scalar(chr(p)) for p in _range(fields[0])) + if not set('0123456789') <= numbers or not set('+<=>') <= maths: + raise ValueError('incomplete numeric or Math property input') + return numbers, maths, {name: sha256(blobs[name]).hexdigest() for name in UCD_FILES} + + + +def _read_unicode_file(path: Path) -> bytes: + """Snapshot a bounded regular file; do not follow symlinks or block on FIFOs.""" + before = path.lstat() + if not stat.S_ISREG(before.st_mode): + raise ValueError(f'Unicode input must be a regular nonsymlink file: {path.name}') + if before.st_size > UCD_MAX_BYTES: + raise ValueError(f'Unicode input exceeds acquisition budget: {path.name}') + flags = os.O_RDONLY | getattr(os, 'O_NOFOLLOW', 0) | getattr(os, 'O_NONBLOCK', 0) + try: + fd = os.open(path, flags) + except OSError as error: + raise ValueError(f'Unicode input changed or cannot be opened: {path.name}') from error + with os.fdopen(fd, 'rb') as stream: + opened = os.fstat(stream.fileno()) + if (not stat.S_ISREG(opened.st_mode) + or (opened.st_dev, opened.st_ino) != (before.st_dev, before.st_ino)): + raise ValueError(f'Unicode input changed or is not regular: {path.name}') + if opened.st_size > UCD_MAX_BYTES: + raise ValueError(f'Unicode input exceeds acquisition budget: {path.name}') + raw = stream.read(UCD_MAX_BYTES + 1) + after = os.fstat(stream.fileno()) + if len(raw) > UCD_MAX_BYTES: + raise ValueError(f'Unicode input exceeds acquisition budget: {path.name}') + if (len(raw) != opened.st_size + or (after.st_size, after.st_mtime_ns) != (opened.st_size, opened.st_mtime_ns)): + raise ValueError(f'Unicode input changed during bounded read: {path.name}') + return raw + + +def _verified_unicode_blobs(ucd: Path) -> dict[str, bytes]: + """Acquire and authenticate a bounded snapshot before any artifact copying.""" + blobs = {name: _read_unicode_file(ucd / name) for name in UCD_FILES} + for name, raw in blobs.items(): + if sha256(raw).hexdigest() != UCD_RELEASE_SHA256[name]: + raise ValueError(f'Unicode release hash mismatch: {name}; complete pinned release required') + return blobs + + +def unicode_admission(ucd: Path) -> tuple[set[str], set[str], dict]: + """Admit only the complete pinned release; never trust a self-declared header.""" + return _parse_unicode_properties(_verified_unicode_blobs(ucd), UNICODE_VERSION) + + +def _layout_repertoire(corpus, legacy, numbers, maths, hashes, unicode_version): + """Lay out already admitted identities; fixture callers must label their source.""" + for c in (*corpus, *legacy): + scalar(c) + if len(set(legacy)) != len(legacy) or not legacy or legacy[0] != ' ': + raise ValueError('legacy anchors must be unique with SPACE first') + admitted = set(corpus) | set(legacy) | numbers | maths | set(LOGIC) + extras = sorted(admitted - set(legacy), key=ord) + order: list[str] = [] + for i, anchor in enumerate(legacy): + order.append(anchor) + order.extend(extras[i * len(extras) // len(legacy): + (i + 1) * len(extras) // len(legacy)]) + if len(order) != len(admitted) or set(order) != admitted: + raise AssertionError('repertoire union lost or duplicated a scalar') + return {'schema': 'english.public-gonol/1', 'unicode_version': unicode_version, + 'layout': LAYOUT, 'normalization': 'none', 'origin_scalar': ' ', + 'corpus_scalars': sorted(corpus, key=ord), 'legacy_anchors': list(legacy), + 'unicode_files_sha256': hashes, 'logical_supplement': list(LOGIC), + 'admission_counts': {'corpus': len(set(corpus)), 'legacy': len(legacy), + 'numbers': len(numbers), 'math': len(maths), + 'logical_supplement': len(set(LOGIC)), + 'additional_to_legacy': len(extras), 'union': len(order)}, + 'identities': order} + + +def repertoire(corpus: tuple[str, ...], legacy: tuple[str, ...], ucd: Path) -> dict: + """Expand the repertoire only after authenticating the complete Unicode release.""" + numbers, maths, hashes = unicode_admission(ucd) + return _layout_repertoire(corpus, legacy, numbers, maths, hashes, UNICODE_VERSION) + + +class Factory: + """Actual recursive constructions; carriers and closed interiors remain available.""" + def __init__(self, public: dict, words: dict[int, str], source_receipt: str): + self.public = json.loads(canonical(public)) + self.words = dict(words) + self.word_ids = {word: wid for wid, word in words.items()} + self.source_receipt = source_receipt + self.glyph_carrier = Carrier(tuple(public['identities']), + f'{PROFILE}:public:{digest(public)}') + ordered = tuple(sorted(words.values(), key=lambda w: tuple( + self.glyph_carrier.index(c) for c in w))) + if not ordered: + raise ValueError('no closed words admitted') + self.word_carrier = Carrier(ordered, f'{PROFILE}:{WORD_LAYOUT}:{source_receipt}') + self._atoms = {c: Participant.atom(self.glyph_carrier, c) + for c in self.glyph_carrier.identities} + + @lru_cache(maxsize=8192) + def word(self, wid: int): + text = self.words[wid] + return affixiate(text, tuple(self._atoms[c] for c in text), origin='SPACE', + relation='ordered-glyphs', + source=f'{self.source_receipt}:word:{wid}') + + def record(self, rid: int, kind: str, parts): + participants = tuple(self._atoms[value] if pkind == 'character' else + Participant.closed(self.word_carrier, self.word(value)) + for pkind, value in parts) + return affixiate(f'{kind}:{rid}', participants, origin='SPACE', + relation=f'ordered-{kind}', + source=f'{self.source_receipt}:record:{rid}') + + +def _inventory(db): + return dict(db.execute('SELECT id,scalar FROM characters ORDER BY id')), dict( + db.execute('SELECT id,surface FROM words ORDER BY id')) + + +def _groups(db): + rows = db.execute('SELECT record_id,ordinal,kind,word_id,character_id ' + 'FROM components ORDER BY record_id,ordinal') + for rid, group in groupby(rows, key=lambda r: r[0]): + yield rid, tuple(group) + + +def _geometry_receipt(db) -> str: + result = sha256() + for table, ordering in (('frames', 'kind,id'), ('placements', 'kind,id,ordinal')): + result.update(canonical(table) + b'\n') + for row in db.execute(f'SELECT * FROM {table} ORDER BY {ordering}'): + result.update(canonical(row) + b'\n') + return result.hexdigest() + + +def _write_frame(db, kind, identity, obj, refs): + db.execute('INSERT INTO frames VALUES(?,?,?,?,?,?,?,?)', + (kind, identity, obj.identity, obj.origin, obj.relation, + obj.source, obj.receipt, len(obj.participants))) + db.executemany('INSERT INTO placements VALUES(?,?,?,?,?,?,?,?)', ( + (kind, identity, i, pkind, pid, + p.carrier.index(p.identity), obj.placement(i).exponent, + None if p.interior is None else p.interior.receipt) + for i, ((pkind, pid), p) in enumerate(zip(refs, obj.participants, strict=True)))) + + +def _json_write(path, value): + Path(path).write_bytes(canonical(value) + b'\n') + + +def _read_bounded_regular(path: Path, limit: int, label: str) -> bytes: + """Snapshot one bounded regular file without following links or blocking on special files.""" + before = path.lstat() + if not stat.S_ISREG(before.st_mode): + raise ValueError(f'{label} must be a regular nonsymlink file: {path.name}') + if before.st_size > limit: + raise ValueError(f'{label} exceeds read budget: {path.name}') + flags = os.O_RDONLY | getattr(os, 'O_NOFOLLOW', 0) | getattr(os, 'O_NONBLOCK', 0) + try: + fd = os.open(path, flags) + except OSError as error: + raise ValueError(f'{label} changed or cannot be opened: {path.name}') from error + with os.fdopen(fd, 'rb') as stream: + opened = os.fstat(stream.fileno()) + if (not stat.S_ISREG(opened.st_mode) + or (opened.st_dev, opened.st_ino) != (before.st_dev, before.st_ino)): + raise ValueError(f'{label} changed or is not regular: {path.name}') + if opened.st_size > limit: + raise ValueError(f'{label} exceeds read budget: {path.name}') + raw = stream.read(limit + 1) + after = os.fstat(stream.fileno()) + if len(raw) > limit: + raise ValueError(f'{label} exceeds read budget: {path.name}') + if (len(raw) != opened.st_size + or (after.st_size, after.st_mtime_ns) != (opened.st_size, opened.st_mtime_ns)): + raise ValueError(f'{label} changed during bounded read: {path.name}') + return raw + + +def _json_read(path): + raw = _read_bounded_regular(Path(path), JSON_MAX_BYTES, 'JSON artifact') + value = json.loads(raw) + if raw != canonical(value) + b'\n': + raise ValueError('noncanonical JSON artifact') + return value + + +def build(admission: Path, ucd: Path, output: Path) -> dict: + """Consume complete source admission, create a fresh self-contained geometry bundle.""" + _producer_check() + if output.exists(): + raise ValueError('output exists; select a new directory') + unicode_blobs = _verified_unicode_blobs(ucd) + from english_gonol.structural_construct import _verified_connection + output.parent.mkdir(parents=True, exist_ok=True) + stage = Path(tempfile.mkdtemp(prefix='.ordinal-', dir=output.parent)) + try: + with _verified_connection(admission) as verified: + source_db, source_manifest = verified + if source_manifest['metadata']['complete_corpus'] is not True: + raise ValueError('replacement construction requires complete pinned source') + (stage / 'admission').mkdir() + with closing(sqlite3.connect(stage / 'admission' / 'construct.db')) as copy: + source_db.backup(copy) + _json_write(stage / 'admission' / 'manifest.json', source_manifest) + (stage / 'ucd').mkdir() + for name in UCD_FILES: + (stage / 'ucd' / name).write_bytes(unicode_blobs[name]) + chars, words = _inventory(source_db) + legacy = tuple(source_manifest['metadata']['producer']['carrier']) + public = repertoire(tuple(chars.values()), legacy, stage / 'ucd') + factory = Factory(public, words, source_manifest['receipt_sha256']) + from english_gonol.ordinal_definitions import build_definitions + definitions = build_definitions(factory) + _json_write(stage / 'character-definitions.json', definitions) + _json_write(stage / 'public-gonol.json', public) + _json_write(stage / 'word-carrier.json', { + 'layout': WORD_LAYOUT, 'identities': list(factory.word_carrier.identities), + 'source': factory.word_carrier.source, 'receipt': factory.word_carrier.receipt}) + character_ids = {c: cid for cid, c in chars.items()} + with closing(sqlite3.connect(stage / 'geometry.db')) as geometry: + geometry.executescript(SQL) + for wid, text in words.items(): + _write_frame(geometry, 'word', wid, factory.word(wid), + tuple(('character', character_ids[c]) for c in text)) + records = dict(source_db.execute('SELECT id,kind FROM records')) + record_count = 0 + for rid, rows in _groups(source_db): + record_count += 1 + obj = factory.record(rid, records[rid], tuple( + (pk, chars[cid] if pk == 'character' else wid) + for _, _, pk, wid, cid in rows)) + refs = tuple((pk, cid if pk == 'character' else wid) + for _, _, pk, wid, cid in rows) + _write_frame(geometry, 'record', rid, obj, refs) + if record_count != len(records): + raise ValueError('source has empty records; empty closure is not licensed') + geometry.commit() + counts = {table: geometry.execute(f'SELECT COUNT(*) FROM {table}').fetchone()[0] + for table in ('frames', 'placements')} + manifest = {'schema': PROFILE, 'source_receipt': source_manifest['receipt_sha256'], + 'source': source_manifest['metadata']['source'], + 'ucns_commit': UCNS_COMMIT, 'implementation_sha256': file_hash(Path(__file__)), + 'public_sha256': digest(public), + 'glyph_carrier_receipt': factory.glyph_carrier.receipt, + 'word_carrier_receipt': factory.word_carrier.receipt, + 'counts': counts, 'geometry_receipt': _geometry_receipt(geometry), + 'character_definitions_receipt': digest(definitions), + 'hmmm': list(MANIFEST_BOUNDARIES)} + _json_write(stage / 'manifest.json', manifest) + report = verify(stage) + _json_write(stage / 'verification.json', report) + stage.rename(output) + return report + except BaseException: + shutil.rmtree(stage, ignore_errors=True) + raise + + +def _independent_frame(kind, frame_id, identity, relation, source_receipt, refs, + factory, chars, word_receipts): + """Reference checker: literal ordinal formula, no Placement/affixiate calls.""" + from fractions import Fraction + positions, stored = [], [] + n = len(refs) + for i, (pk, pid) in enumerate(refs): + carrier = factory.glyph_carrier if pk == 'character' else factory.word_carrier + name = chars[pid] if pk == 'character' else factory.words[pid] + index = carrier.index(name) + turn = Fraction(index, len(carrier.identities)) + interior = None if pk == 'character' else word_receipts[pid] + positions.append({'identity': name, 'carrier_receipt': carrier.receipt, + 'index': index, 'arity': len(carrier.identities), + 'turn': [turn.numerator, turn.denominator], + 'radius': {'numerator': 1, 'power_of_two_denominator': n-i}, + 'interior_receipt': interior}) + stored.append((kind, frame_id, i, pk, pid, index, n-i, interior)) + source = f'{source_receipt}:{kind}:{frame_id}' + receipt = digest({'schema': 'ucns.ordinal-affixiation.v1', 'identity': identity, + 'origin': 'SPACE', 'relation': relation, 'source': source, + 'placements': positions}) + return (kind, frame_id, identity, 'SPACE', relation, source, receipt, n), stored + + +def _validate_manifest(manifest: object) -> None: + """Reject unsupported claims before source replay; this alone proves no geometry.""" + if type(manifest) is not dict or set(manifest) != MANIFEST_KEYS: + raise ValueError('manifest schema keys differ from the exact profile') + if manifest['schema'] != PROFILE or manifest['ucns_commit'] != UCNS_COMMIT: + raise ValueError('profile/geometry producer mismatch') + if manifest['implementation_sha256'] != file_hash(Path(__file__)): + raise ValueError('implementation changed; rebuild explicitly') + if manifest['hmmm'] != list(MANIFEST_BOUNDARIES): + raise ValueError('manifest boundary claims differ from the exact profile') + counts = manifest['counts'] + if (type(counts) is not dict or set(counts) != {'frames', 'placements'} + or any(type(v) is not int or v <= 0 for v in counts.values())): + raise ValueError('manifest counts must be positive integer frame/placement counts') + for key in ('source_receipt', 'public_sha256', 'glyph_carrier_receipt', + 'word_carrier_receipt', 'geometry_receipt', 'character_definitions_receipt'): + value = manifest[key] + if (type(value) is not str or len(value) != 64 + or any(c not in '0123456789abcdef' for c in value)): + raise ValueError(f'manifest digest is invalid: {key}') + + +def verify(output: Path) -> dict: + """Replay every source and placement; structural bytes alone never prove new geometry.""" + _producer_check() + manifest = _json_read(output / 'manifest.json') + _validate_manifest(manifest) + from english_gonol.structural_construct import _verified_connection, open_readonly, _schema + with _verified_connection(output / 'admission') as verified: + source, source_manifest = verified + if not source_manifest['metadata']['complete_corpus']: + raise ValueError('partial source cannot verify the replacement') + if (manifest['source_receipt'] != source_manifest['receipt_sha256'] or + manifest['source'] != source_manifest['metadata']['source']): + raise ValueError('source binding mismatch') + chars, words = _inventory(source) + public = repertoire(tuple(chars.values()), tuple( + source_manifest['metadata']['producer']['carrier']), output / 'ucd') + if public != _json_read(output / 'public-gonol.json') or digest(public) != manifest['public_sha256']: + raise ValueError('public gonol mismatch') + factory = Factory(public, words, manifest['source_receipt']) + expected_words = {'layout': WORD_LAYOUT, 'identities': list(factory.word_carrier.identities), + 'source': factory.word_carrier.source, 'receipt': factory.word_carrier.receipt} + if _json_read(output / 'word-carrier.json') != expected_words: + raise ValueError('word carrier mismatch') + if (factory.glyph_carrier.receipt != manifest['glyph_carrier_receipt'] or + factory.word_carrier.receipt != manifest['word_carrier_receipt']): + raise ValueError('carrier receipt mismatch') + from english_gonol.ordinal_definitions import verify_definitions + definitions = _json_read(output / 'character-definitions.json') + if digest(definitions) != manifest['character_definitions_receipt']: + raise ValueError('character definition receipt mismatch') + definition_layer = verify_definitions(factory, definitions) + word_receipts = {} + character_ids = {c: cid for cid, c in chars.items()} + with closing(open_readonly(output / 'geometry.db')) as db: + db.execute('BEGIN') + with closing(sqlite3.connect(':memory:')) as expected: + expected.executescript(SQL) + if _schema(db) != _schema(expected): + raise ValueError('geometry schema mismatch') + if db.execute('PRAGMA integrity_check').fetchone() != ('ok',) or db.execute('PRAGMA foreign_key_check').fetchone(): + raise ValueError('geometry integrity failure') + frames_checked = placements_checked = 0 + + def check(kind, frame_id, identity, relation, refs): + nonlocal frames_checked, placements_checked + frame, positions = _independent_frame(kind, frame_id, identity, relation, + manifest['source_receipt'], refs, factory, chars, word_receipts) + actual_frame = db.execute('SELECT * FROM frames WHERE kind=? AND id=?', (kind, frame_id)).fetchone() + actual = db.execute('SELECT * FROM placements WHERE kind=? AND id=? ORDER BY ordinal', + (kind, frame_id)).fetchall() + if actual_frame != frame or actual != positions: + raise ValueError(f'geometry mismatch at {kind}:{frame_id}') + frames_checked += 1 + placements_checked += len(positions) + return frame[6] + + for wid, text in words.items(): + word_receipts[wid] = check('word', wid, text, 'ordered-glyphs', + tuple(('character', character_ids[c]) for c in text)) + records = dict(source.execute('SELECT id,kind FROM records')) + for rid, rows in _groups(source): + check('record', rid, f'{records[rid]}:{rid}', f'ordered-{records[rid]}', + tuple((pk, cid if pk == 'character' else wid) for _, _, pk, wid, cid in rows)) + counts = {table: db.execute(f'SELECT COUNT(*) FROM {table}').fetchone()[0] + for table in ('frames', 'placements')} + if (frames_checked != len(words) + len(records) or counts != manifest['counts'] + or counts != {'frames': frames_checked, 'placements': placements_checked}): + raise ValueError('missing or extra geometry') + if _geometry_receipt(db) != manifest['geometry_receipt']: + raise ValueError('geometry receipt mismatch') + return {'schema': 'english.ordinal-verification/1', 'status': 'SURVIVED', + 'scope': 'complete pinned corpus; source recovery plus every local placement', + 'public_gonol_size': len(public['identities']), 'words': len(words), + **counts, 'character_definitions': dict(definition_layer.counts), + 'manifest_sha256': digest(manifest), + 'source_receipt': manifest['source_receipt'], 'hmmm': list(MANIFEST_BOUNDARIES)} + + +def query(output: Path, text: str) -> dict: + """Construct exact ordered text from the sealed admission; never admit unknown words.""" + from english_gonol.structural_construct import _verified_connection + from english_gonol.structural_rules import segment + report = verify(output) + geometry_manifest = _json_read(output / 'manifest.json') + if digest(geometry_manifest) != report['manifest_sha256']: + raise ValueError('manifest changed after verification') + with _verified_connection(output / 'admission') as verified: + source, manifest = verified + _, words = _inventory(source) + public = _json_read(output / 'public-gonol.json') + if (digest(public) != geometry_manifest['public_sha256'] or + manifest['receipt_sha256'] != geometry_manifest['source_receipt']): + raise ValueError('query inputs changed after verification') + factory = Factory(public, words, manifest['receipt_sha256']) + parts = segment(text, manifest['metadata']['profile']['abbreviations']) + participants = [] + interiors = {} + for part in parts: + if part.kind == 'word': + if part.text not in factory.word_ids: + raise ValueError(f'word not admitted: {part.text!r}') + word = factory.word(factory.word_ids[part.text]) + participants.append(Participant.closed(factory.word_carrier, word)) + interiors[word.receipt] = word.record() + else: + if part.text not in factory._atoms: + raise ValueError(f'scalar not admitted: U+{ord(part.text):04X}') + participants.append(factory._atoms[part.text]) + obj = affixiate('query', tuple(participants), origin='SPACE', relation='ordered-text', + source=f'query:{digest(text)}:{manifest["receipt_sha256"]}') + recovered = ''.join(obj.recover()) + if recovered != text: + raise AssertionError('query source recovery failed') + return {'construction': obj.record(), 'interiors': interiors, 'recovered': recovered, + 'public_gonol_sha256': digest(factory.public), + 'word_carrier_receipt': factory.word_carrier.receipt, + 'closure': 'ordered-text; no grammatical or semantic assertion'} + + +def main() -> None: + parser = argparse.ArgumentParser(description=__doc__) + sub = parser.add_subparsers(dest='command', required=True) + builder = sub.add_parser('build') + for name in ('admission', 'ucd', 'output'): + builder.add_argument(name, type=Path) + checker = sub.add_parser('verify') + checker.add_argument('output', type=Path) + request = sub.add_parser('query') + request.add_argument('output', type=Path) + request.add_argument('text') + args = parser.parse_args() + if args.command == 'build': + value = build(args.admission, args.ucd, args.output) + elif args.command == 'verify': + value = verify(args.output) + else: + value = query(args.output, args.text) + print(json.dumps(value, ensure_ascii=False, indent=2)) + + +if __name__ == '__main__': + main() diff --git a/research/english-gonol/english_gonol/ordinal_definitions.py b/research/english-gonol/english_gonol/ordinal_definitions.py new file mode 100644 index 00000000..2f12f694 --- /dev/null +++ b/research/english-gonol/english_gonol/ordinal_definitions.py @@ -0,0 +1,216 @@ +"""Reconstruct inherited character definitions on the selected ordinal base. + +The existing character table and final-y implementation remain their owners. +Only data/behavior is consumed from that historical module; its constructor is +never called. Every admitted scalar receives its prior entry or explicit hmmm. + +Usage with an ordinal_construct.Factory: + payload = build_definitions(factory) + layer = verify_definitions(factory, payload) + assert layer.final_y_realization('y', 'r', 'ing', True) == 'preserve-y' + +The main ordinal build owns persistence. This file neither mutates a carrier nor +admits a missing number-name word. Pronunciation/numerology remain source-owned +candidate definition content, not added identity rules or validated science. +""" +from __future__ import annotations + +from dataclasses import asdict, dataclass +from fractions import Fraction +from hashlib import sha256 +import json +from pathlib import Path +from types import MappingProxyType +from typing import Mapping + +from ucns.ordinal_affixiation import Participant, affixiate +from .language import character_definitions as inherited + +# === MODULE_BUILD === +# id: english_ordinal_inherited_definitions +# module_name: ordinal_definitions +# module_kind: engine +# summary: reuse inherited definitions and behavior, reconstruct with UCNS ordinal geometry +# owner: Erin Spencer +# public_surface: build_definitions, verify_definitions, VerifiedDefinitions +# internal_surface: inherited source admission, relation iteration, independent placement replay +# auth_boundary: read +# user_data_boundary: none +# admin_only: false +# network_boundary: none +# storage_boundary: read +# tests: tests/test_ordinal_definitions.py +# rollout: constitutive required component of the selected ordinal artifact +# rollback: rebuild explicitly; no historical constructor fallback +# unresolved: source-uncovered definitions remain hmmm; global attachment transport is separate +# === END MODULE_BUILD === +# === CONTRACTS === +# id: ordinal_inherited_definition_preservation +# given: the existing character definition source and the expanded public carrier +# then: retain entries, overlapping roles, word links, order, provenance and explicit unknowns +# id: ordinal_definition_replay +# given: serialized character and definition closures on the selected carriers +# then: independently replay every placement and interior without invoking affixiate +# === END CONTRACTS === + +SCHEMA = 'english.ordinal-character-definitions/1' +SOURCE_PATH = 'english_gonol/language/data/character_definitions_v1.json' + + +def _canonical(value): + return json.dumps(value, ensure_ascii=False, sort_keys=True, + separators=(',', ':'), allow_nan=False).encode('utf-8') + + +def _digest(value): + return sha256(_canonical(value)).hexdigest() + + +def _inputs(factory): + path = Path(inherited.__file__).parent / 'data' / 'character_definitions_v1.json' + raw = path.read_bytes() + entries = inherited.load_character_definition_table(path) + # Source is local repository input, not an artifact's self-declared authority. + if path.read_bytes() != raw: + raise ValueError('definition source changed while loading') + admitted = factory.glyph_carrier.identities + if any(c not in admitted for c, e in entries.items() if not e.hmmm): + raise ValueError('curated definition scalar is not admitted by this carrier') + current = {c: entries.get(c, inherited.CharacterDefinitionEntry( + c, (), None, (), None, (), True)) for c in admitted} + missing = {e.number_name for e in current.values() + if e.number_name and e.number_name not in factory.word_ids} + if missing: + raise ValueError('definition number-name words are not admitted: ' + + ', '.join(sorted(missing))) + return current, { + 'path': SOURCE_PATH, 'sha256': sha256(raw).hexdigest(), + 'text': raw.decode('utf-8'), + 'loader_sha256': sha256(Path(inherited.__file__).read_bytes()).hexdigest(), + } + + +def _relations(char, entry): + """Preserve the prior builder's relation order and source identifiers.""" + for role in entry.classes: + yield f'char-def:{char}:{role}', f'char-class:{role}', None + if entry.pronunciation: + yield (f'char-def:{char}:letter-pronunciation', + f'letter-pronunciation:{entry.pronunciation}', None) + for behavior in entry.orthographic: + yield (f'char-def:{char}:orthographic:{behavior}', + f'orthographic-behavior:{behavior}', None) + if entry.number_name: + yield f'char-def:{char}:number-name', 'digit-number-name', entry.number_name + for index, meaning in enumerate(entry.numerology): + yield f'char-def:{char}:numerology:{index}', f'numerology:{meaning}', None + if entry.hmmm: + yield f'char-def:{char}:hmmm', 'definition:hmmm', None + + +def _reference_frame(identity, participants, relation, source): + """Independent exact formula; never calls affixiate, Placement or record.""" + placements = [] + for i, (carrier, name, interior) in enumerate(participants): + index = carrier.index(name) + turn = Fraction(index, len(carrier.identities)) + placements.append({ + 'identity': name, 'carrier_receipt': carrier.receipt, + 'index': index, 'arity': len(carrier.identities), + 'turn': [turn.numerator, turn.denominator], + 'radius': {'numerator': 1, + 'power_of_two_denominator': len(participants) - i}, + 'interior_receipt': None if interior is None else interior['receipt'], + }) + record = {'schema': 'ucns.ordinal-affixiation.v1', 'identity': identity, + 'origin': 'SPACE', 'relation': relation, 'source': source, + 'placements': placements} + return {'receipt': _digest(record), 'construction': record} + + +def _construct(factory, *, reference): + entries, source = _inputs(factory) + prefix = f'{factory.source_receipt}:definitions:{source["sha256"]}' + objects = {} + + def frame(identity, participants, relation, source_id): + if reference: + return _reference_frame(identity, participants, relation, source_id) + actual = tuple(Participant.atom(carrier, name) if interior is None else + Participant.closed(carrier, objects[interior['receipt']]) + for carrier, name, interior in participants) + closed = affixiate(identity, actual, origin='SPACE', relation=relation, + source=source_id) + objects[closed.receipt] = closed + return {'receipt': closed.receipt, 'construction': closed.record()} + + characters, words, definitions, registry = {}, {}, [], [] + for char, entry in entries.items(): + character = frame(char, ((factory.glyph_carrier, char, None),), + 'character', f'{prefix}:char:{char}') + characters[char] = character + owned = [] + for source_id, relation, name in _relations(char, entry): + participants = [(factory.glyph_carrier, char, character)] + if name is not None: + if name not in words: + # Use the existing closed word's exact identity and source. + wid = factory.word_ids[name] + words[name] = frame(name, tuple( + (factory.glyph_carrier, c, None) for c in name), + 'ordered-glyphs', f'{factory.source_receipt}:word:{wid}') + participants.append((factory.word_carrier, name, words[name])) + definition = frame(source_id, tuple(participants), relation, + f'{prefix}:{source_id}') + owned.append(definition['receipt']) + definitions.append({'owner': char, 'source_id': source_id, **definition}) + registry.append({'scalar': char, 'entry': asdict(entry), + 'character_receipt': character['receipt'], + 'definition_receipts': owned}) + payload = { + 'schema': SCHEMA, 'profile': 'english.ordinal/1', + 'source_receipt': factory.source_receipt, 'source': source, + 'implementation_sha256': sha256(Path(__file__).read_bytes()).hexdigest(), + 'glyph_carrier_receipt': factory.glyph_carrier.receipt, + 'word_carrier_receipt': factory.word_carrier.receipt, + 'entries': registry, 'characters': characters, + 'number_names': words, 'definitions': definitions, + 'counts': {'characters': len(characters), 'definitions': len(definitions), + 'number_names': len(words), + 'unresolved': sum(e.hmmm for e in entries.values())}, + 'hmmm': ['Uncovered scalar definitions remain explicit hmmm.', + 'Local SPACE frames do not prove global attachment transport.', + 'Inherited table content remains candidate evidence, not linguistic or cultural canon.'], + } + # Normalize dataclass tuples once to the actual JSON representation. + return json.loads(_canonical(payload)), entries + + +def build_definitions(factory): + """Construct every current-carrier definition; no old geometry fallback.""" + return _construct(factory, reference=False)[0] + + +@dataclass(frozen=True) +class VerifiedDefinitions: + """Verified immutable entries retain the prior implementation's behavior.""" + entries: Mapping[str, inherited.CharacterDefinitionEntry] + receipt: str + counts: Mapping[str, int] + + def final_y_realization(self, final_character_surface, + preceding_character_surface, suffix_bare, + suffix_vowel_initial): + return inherited.CharacterDefinitionLayer.final_y_realization( + self, final_character_surface, preceding_character_surface, + suffix_bare, suffix_vowel_initial) + + +def verify_definitions(factory, payload): + """Check complete coverage, content, receipts, all local placements and links.""" + expected, entries = _construct(factory, reference=True) + # Canonical bytes distinguish booleans from integer placement/count fields. + if _canonical(payload) != _canonical(expected): + raise ValueError('ordinal inherited-definition replay mismatch') + return VerifiedDefinitions(MappingProxyType(entries), _digest(expected), + MappingProxyType(expected['counts'])) diff --git a/research/english-gonol/experiments/ordinal-definitions/migration-witness.json b/research/english-gonol/experiments/ordinal-definitions/migration-witness.json new file mode 100644 index 00000000..f22ff54d --- /dev/null +++ b/research/english-gonol/experiments/ordinal-definitions/migration-witness.json @@ -0,0 +1,31 @@ +{ + "schema": "english.ordinal-definition-migration-witness/1", + "status": "SURVIVED", + "scope": "Every expanded-carrier character and inherited definition; all ten number-name interiors match prior complete corpus word receipts", + "public_gonol_size": 4698, + "source_words": 103053, + "source_receipt": "26ab095f73306ff4fbd9f67d1c282c1df79f926bb75cc081c879db8a6989b58a", + "public_gonol_sha256": "dc8740a258b95688882b70d4faaa8b96b8f903990f459f4804e779879fca043d", + "definition_receipt": "4889437942cbbc9b9c6d7cb85f466b753669ac2169f6eda3da621ba96c4ed80e", + "counts": { + "characters": 4698, + "definitions": 4757, + "number_names": 10, + "unresolved": 4643 + }, + "behavior": { + "hyphen_roles": ["operator", "punctuation"], + "3_number_name": "three", + "try_ing": "preserve-y", + "try_ed": "y-to-i" + }, + "source_table_sha256": "ba431162c065a033d60f66bfb84d62d93f55ca6ca954ec59756c4bacc854827d", + "source_loader_sha256": "0fe9c8a0ee263c008e530e3df2b521ad4ccb08c50c59939872d7b6fcb9f8e67c", + "implementation_sha256": "8fac12851ab018ccc6355dddf8bf67b52e70706a21c9020ad8dc555a9395d8f9", + "local_checks": {"passed": 85, "failed": 0, "scope": "new definition tests, current ordinal tests, packaged pinned UCNS ordinal tests; unused historical constructor import poisoned in local harness"}, + "hmmm": [ + "Uses supplied complete source/geometry databases, not current whole-repository CI or a new full-corpus admission replay.", + "New scalar definitions absent from the inherited source remain explicitly unresolved.", + "SPACE local-frame and definition-source links are preserved; global geometric transport is not established." + ] +} diff --git a/research/english-gonol/tests/test_active_construction.py b/research/english-gonol/tests/test_active_construction.py new file mode 100644 index 00000000..999b72ae --- /dev/null +++ b/research/english-gonol/tests/test_active_construction.py @@ -0,0 +1,222 @@ +"""Run with pytest; routing witnesses do not claim complete-corpus replay.""" +# === CHECKS === +# id: english_active_routing_regression +# proves: english_active_no_legacy_fallback +# call: self::test_only_ordinal_routes +# requires: python3, pytest +# timeout: 60 +# mutates: monkeypatched in-memory functions only +# cleanup: pytest monkeypatch +# id: english_active_profile_regression +# proves: english_profile_without_compute +# call: self::test_profile_and_help_do_not_load_constructor +# requires: python3, pytest +# timeout: 60 +# mutates: isolated subprocess only +# cleanup: process exit +# === END CHECKS === +import json +from pathlib import Path +import subprocess +import sys + +import pytest +from english_gonol import __main__ as entry +from english_gonol import ordinal_construct + + +@pytest.mark.parametrize('args,method,expected', [ + (['build', 'admission', 'ucd', 'output'], 'build', (Path('admission'), Path('ucd'), Path('output'))), + (['verify', 'output'], 'verify', (Path('output'),)), + (['query', 'output', 'the geometry'], 'query', (Path('output'), 'the geometry')), +]) +def test_only_ordinal_routes(monkeypatch, capsys, args, method, expected): + calls = [] + def witness(*values): + calls.append(values) + return {'called': method} + monkeypatch.setattr(ordinal_construct, method, witness) + entry.main(args) + assert calls == [expected] + assert json.loads(capsys.readouterr().out) == {'called': method} + + +@pytest.mark.parametrize('command', ['legacy', 'structural', 'full-v2']) +def test_retired_commands_refuse(command): + with pytest.raises(SystemExit) as error: + entry.main([command]) + assert error.value.code == 2 + + +def test_selection_drift_refuses(monkeypatch): + selection = entry.active_profile() + selection['active_profile'] = 'retired-profile' + monkeypatch.setattr(entry, 'active_profile', lambda: selection) + with pytest.raises(ValueError, match='no legacy fallback'): + entry.main(['verify', 'never-opened']) + + +def test_profile_and_help_do_not_load_constructor(): + for args in (['profile'], ['--help']): + script = ("import sys; from english_gonol.__main__ import main\n" + "try: main(" + repr(args) + ")\n" + "except SystemExit as exc: assert exc.code == 0\n" + "assert 'english_gonol.ordinal_construct' not in sys.modules\n") + result = subprocess.run([sys.executable, '-c', script], text=True, capture_output=True) + assert result.returncode == 0, result.stderr + assert result.stdout + + +def test_active_selection_matches_exact_constructor(): + profile = entry.active_profile() + assert profile['active_profile'] == ordinal_construct.PROFILE + assert profile['geometry_commit'] == ordinal_construct.UCNS_COMMIT + assert profile['fallback'] == 'none' + assert profile['deprecated_as_completed_geometry'] + assert profile['hmmm'] + + +def test_active_inputs_are_registered_in_root_provenance(): + import hashlib + workspace = Path(entry.__file__).resolve().parents[1] + root = workspace.parents[1] + profile = entry.active_profile() + manifest = json.loads((root / 'stack-manifest.json').read_text()) + base = json.loads((workspace / 'BASE.json').read_text()) + graph = json.loads((workspace / profile['input_graph']).read_text()) + human = (root / 'STACK_MANIFEST.md').read_text() + records = [r for r in manifest['research_participants'] + if r['workspace'] == 'research/english-gonol/'] + keyed = {r['participant_id']: r for r in records} + assert len(keyed) == len(records), 'duplicate source-qualified participant' + exact_identity = lambda r: r.get('commit') or r.get('artifact_sha256') + source_pairs = {(r['repository'], exact_identity(r)) for r in records} + assert all((r['repository'], exact_identity(r)) in source_pairs + for r in graph['repositories']) + assert keyed['ordinal-geometry']['commit'] == profile['geometry_commit'] + assert base['ucns_prerequisite']['commit'] == profile['geometry_commit'] + assert base['active_construction']['selection'] == 'ACTIVE_CONSTRUCTION.json' + assert base['active_construction']['source_commit'] == keyed['ordinal-source']['commit'] + assert all(r['authority_transfer'] is False for r in records) + payload = {k: manifest[k] for k in ('repositories', 'research_participants', 'boundaries')} + digest = hashlib.sha256(json.dumps(payload, sort_keys=True, separators=(',', ':')).encode()).hexdigest() + assert digest == manifest['work_graph_sha256'] + assert digest in human + for participant_id, record in keyed.items(): + assert exact_identity(record) in human + if participant_id.startswith('ordinal-'): + assert participant_id in human + assert keyed['ordinal-unicode-data']['artifact_sha256'] == implementation.UCD_RELEASE_SHA256['UnicodeData.txt'] + assert keyed['ordinal-unicode-math']['artifact_sha256'] == implementation.UCD_RELEASE_SHA256['DerivedCoreProperties.txt'] + + +def test_full_workflow_covers_admission_dependencies(): + from fnmatch import fnmatch + import yaml + workspace = Path(entry.__file__).resolve().parents[1] + root = workspace.parents[1] + workflow = yaml.load((root / '.github/workflows/english-ordinal-affixiation.yml').read_text(), + Loader=yaml.BaseLoader) + filters = workflow['on']['pull_request']['paths'] + closure = {'structural_construct.py', 'structural_rules.py', 'structural_query.py'} + closure.update(p.name for p in (workspace / 'english_gonol').glob('*.py')) + for name in closure: + path = f'research/english-gonol/english_gonol/{name}' + assert any(fnmatch(path, pattern) for pattern in filters), path + + +def test_workflow_tests_merge_tree_and_binds_selected_source_bytes(): + import yaml + workspace = Path(entry.__file__).resolve().parents[1] + root = workspace.parents[1] + workflow = yaml.load((root / '.github/workflows/english-ordinal-affixiation.yml').read_text(), + Loader=yaml.BaseLoader) + steps = workflow['jobs']['full-corpus']['steps'] + checkout = next(step for step in steps if step.get('uses', '').startswith('actions/checkout@')) + assert 'ref' not in checkout.get('with', {}), 'PR tests must use the generated merge tree' + graph_check = next(step['run'] for step in steps + if step.get('name') == 'Verify exact participant graph and unchanged source reader') + assert "['active_construction']['source_commit']" in graph_check + assert "['git','show',selected+':'+path]" in graph_check + assert "pathlib.Path(path).read_bytes()==subprocess.check_output" in graph_check + acquisition = next(step for step in steps + if step.get('name') == 'Acquire declared Unicode release and license') + assert 'stack-tested-merge.txt' in acquisition['run'] + assert 'SOURCE_HEAD' in acquisition['env'] + + +def test_provenance_repins_trigger_exact_source_gate(): + from fnmatch import fnmatch + import yaml + root = Path(entry.__file__).resolve().parents[3] + workflow = yaml.load((root / '.github/workflows/english-ordinal-affixiation.yml').read_text(), + Loader=yaml.BaseLoader) + filters = workflow['on']['pull_request']['paths'] + for path in ('research/english-gonol/BASE.json', 'stack-manifest.json', + 'STACK_MANIFEST.md', 'research/english-gonol/ACTIVE_CONSTRUCTION.json', + 'research/english-gonol/docs/ordinal-work-graph.json'): + assert any(fnmatch(path, pattern) for pattern in filters), path + + +def test_selection_and_source_graph_are_exact_pinned_inputs(): + import ast + import yaml + root = Path(entry.__file__).resolve().parents[3] + workflow = yaml.load((root / '.github/workflows/english-ordinal-affixiation.yml').read_text(), + Loader=yaml.BaseLoader) + steps = workflow['jobs']['full-corpus']['steps'] + command = next(step['run'] for step in steps + if step.get('name') == 'Verify exact participant graph and unchanged source reader') + python = command.split("<<'PY'\n", 1)[1].rsplit('\nPY', 1)[0] + tree = ast.parse(python) + loop = next(node for node in ast.walk(tree) if isinstance(node, ast.For) + and isinstance(node.target, ast.Name) and node.target.id == 'relative') + inputs = ast.literal_eval(loop.iter) + assert set(inputs) == {'english_gonol/ordinal_construct.py', 'english_gonol/__main__.py', + 'ACTIVE_CONSTRUCTION.json', 'docs/ordinal-work-graph.json'} + assert any(isinstance(node, ast.Assert) for node in loop.body) + + +def test_auxiliary_source_graph_is_outside_verified_construction(): + import shlex + import yaml + root = Path(entry.__file__).resolve().parents[3] + workflow = yaml.load((root / '.github/workflows/english-ordinal-affixiation.yml').read_text(), + Loader=yaml.BaseLoader) + copies = [] + for step in workflow['jobs']['full-corpus']['steps']: + for line in step.get('run', '').splitlines(): + if line.strip().startswith('cp '): + words = shlex.split(line) + if 'research/english-gonol/docs/ordinal-work-graph.json' in words: + copies.append(words[-1]) + assert copies == ['_evidence/source-work-graph.json'] + + +def test_full_workflow_uses_baseline_owned_admission_inventory(): + import yaml + root = Path(entry.__file__).resolve().parents[3] + workflow = yaml.load((root / '.github/workflows/english-ordinal-affixiation.yml').read_text(), + Loader=yaml.BaseLoader) + steps = workflow['jobs']['full-corpus']['steps'] + command = next(step['run'] for step in steps + if step.get('name') == 'Verify exact participant graph and unchanged source reader') + assert "git','show',base+':'+baseline_path" in command + assert "ast.literal_eval(assignment.value)" in command + assert "from english_gonol.structural_construct import IMPLEMENTATION_FILES" not in command + + +def test_general_workflow_isolates_ordinal_ucns_package(): + import yaml + root = Path(entry.__file__).resolve().parents[3] + workflow = yaml.load((root / '.github/workflows/english-gonol.yml').read_text(), + Loader=yaml.BaseLoader) + steps = workflow['jobs']['english-gonol-tests']['steps'] + install = next(step['run'] for step in steps if step.get('name') == 'Install construction prerequisites') + historical = next(step['run'] for step in steps if step.get('name') == 'Historical English Gonol tests') + ordinal = next(step['run'] for step in steps if step.get('name') == 'Isolated ordinal construction tests') + assert './_deps/ucns-ordinal' not in install.splitlines()[0] + assert '_deps/ordinal-venv' in install + assert '--ignore=tests/test_ordinal_construct.py' in historical + assert '--ignore=tests/test_active_construction.py' in historical + assert '../../_deps/ordinal-venv/bin/python' in ordinal diff --git a/research/english-gonol/tests/test_ordinal_construct.py b/research/english-gonol/tests/test_ordinal_construct.py new file mode 100644 index 00000000..cdb588de --- /dev/null +++ b/research/english-gonol/tests/test_ordinal_construct.py @@ -0,0 +1,465 @@ +# === CHECKS === +# id: english_ordinal_union_and_layout +# proves: english_expanded_repertoire +# call: self::test_union_and_layout +# requires: python3, pytest +# timeout: 60 +# mutates: temporary fixture files only +# cleanup: pytest tmp_path +# id: english_ordinal_supplementary_scalar_not_utf16_units +# proves: english_expanded_repertoire +# call: self::test_supplementary_scalar_not_utf16_units +# requires: python3, pytest +# timeout: 60 +# mutates: temporary fixture files only +# cleanup: pytest tmp_path +# id: english_ordinal_no_normalization +# proves: english_expanded_repertoire +# call: self::test_no_normalization +# requires: python3, pytest +# timeout: 60 +# mutates: temporary fixture files only +# cleanup: pytest tmp_path +# id: english_ordinal_wrong_version_refuses +# proves: english_expanded_repertoire +# call: self::test_wrong_version_refuses +# requires: python3, pytest +# timeout: 60 +# mutates: temporary fixture files only +# cleanup: pytest tmp_path +# id: english_ordinal_missing_property_data_refuses +# proves: english_expanded_repertoire +# call: self::test_missing_property_data_refuses +# requires: python3, pytest +# timeout: 60 +# mutates: temporary fixture files only +# cleanup: pytest tmp_path +# id: english_ordinal_unclosed_unicode_range_refuses +# proves: english_expanded_repertoire +# call: self::test_unclosed_unicode_range_refuses +# requires: python3, pytest +# timeout: 60 +# mutates: temporary fixture files only +# cleanup: pytest tmp_path +# id: english_ordinal_invalid_scalar_and_anchor_refusal +# proves: english_expanded_repertoire +# call: self::test_invalid_scalar_and_anchor_refusal +# requires: python3, pytest +# timeout: 60 +# mutates: temporary fixture files only +# cleanup: pytest tmp_path +# id: english_ordinal_word_and_recursive_record +# proves: english_recursive_ordinal_geometry +# call: self::test_word_and_recursive_record +# requires: python3, pytest +# timeout: 60 +# mutates: temporary fixture files only +# cleanup: pytest tmp_path +# id: english_ordinal_append_does_not_change_angle +# proves: english_recursive_ordinal_geometry +# call: self::test_append_does_not_change_angle +# requires: python3, pytest +# timeout: 60 +# mutates: temporary fixture files only +# cleanup: pytest tmp_path +# id: english_ordinal_sql_materialization_and_independent_replay +# proves: english_complete_geometry_replay +# call: self::test_sql_materialization_and_independent_replay +# requires: python3, pytest +# timeout: 60 +# mutates: temporary fixture files only +# cleanup: pytest tmp_path +# id: english_ordinal_unknown_word_refuses +# proves: english_recursive_ordinal_geometry +# call: self::test_unknown_word_refuses +# requires: python3, pytest +# timeout: 60 +# mutates: temporary fixture files only +# cleanup: pytest tmp_path +# id: english_ordinal_geometry_producer_bytes_are_bound +# proves: english_complete_geometry_replay +# call: self::test_geometry_producer_bytes_are_bound +# requires: python3, pytest +# timeout: 60 +# mutates: temporary fixture files only +# cleanup: pytest tmp_path +# id: english_ordinal_release_hash_binding +# proves: english_expanded_repertoire +# call: self::test_fixture_cannot_claim_production_release +# requires: python3, pytest +# timeout: 60 +# mutates: temporary fixture files only +# cleanup: pytest tmp_path +# id: english_ordinal_manifest_claim_binding +# proves: english_complete_geometry_replay +# call: self::test_modified_manifest_refuses_before_source_replay +# requires: python3, pytest +# timeout: 60 +# mutates: temporary fixture files only +# cleanup: pytest tmp_path +# === END CHECKS === + +"""Fixture tests are not full Unicode or corpus receipts; hosted build supplies those.""" +from fractions import Fraction +import sqlite3 +import pytest +from english_gonol.ordinal_construct import ( + Factory, LOGIC, SQL, UCD_FILES, scalar, _parse_unicode_properties, _layout_repertoire, + _write_frame, _independent_frame, _producer_check, +) +from ucns.ordinal_affixiation import Carrier, Participant, affixiate +from english_gonol import ordinal_construct as implementation + +FIXTURE_VERSION = 'fixture-not-a-unicode-release' + + +def ucd_fixture(tmp_path): + rows = [] + for c in '0123456789': + rows.append(';'.join([f'{ord(c):04X}', 'DIGIT', 'Nd'] + [''] * 12)) + rows.extend([';'.join(['00B2', 'SUPERSCRIPT TWO', 'No'] + [''] * 12), + ';'.join(['2160', 'ROMAN NUMERAL ONE', 'Nl'] + [''] * 12)]) + (tmp_path / 'UnicodeData.txt').write_text('\n'.join(rows) + '\n') + (tmp_path / 'DerivedCoreProperties.txt').write_text( + f'# DerivedCoreProperties-{FIXTURE_VERSION}.txt\n002B ; Math\n003C..003E ; Math\n2200 ; Math\n') + return tmp_path + + +def parse_fixture(path): + return _parse_unicode_properties({name: (path / name).read_bytes() for name in UCD_FILES}, + FIXTURE_VERSION) + + +def fixture_repertoire(corpus, legacy, path): + numbers, maths, hashes = parse_fixture(path) + return _layout_repertoire(corpus, legacy, numbers, maths, hashes, FIXTURE_VERSION) + + +def factory_fixture(tmp_path): + public = fixture_repertoire(tuple(' ABCé\u0301'), tuple(' ABC'), ucd_fixture(tmp_path)) + return Factory(public, {1: 'ABA', 2: 'C', 3: 'é', 4: 'A\u0301'}, 'fixture-source') + + +def test_union_and_layout(tmp_path): + ucd = ucd_fixture(tmp_path) + public = fixture_repertoire(tuple(' é\u0301'), tuple(' ABC'), ucd) + values = public['identities'] + assert values[0] == ' ' + assert [c for c in values if c in ' ABC'] == list(' ABC') + assert set('0123456789²Ⅰé\u0301') | set(LOGIC) <= set(values) + assert len(values) == len(set(values)) == public['admission_counts']['union'] + anchors = [values.index(c) for c in ' ABC'] + [len(values)] + counts = [b-a-1 for a,b in zip(anchors, anchors[1:])] + assert max(counts) - min(counts) <= 1 + assert public == fixture_repertoire(tuple(' é\u0301'), tuple(' ABC'), ucd) + + +def test_supplementary_scalar_not_utf16_units(tmp_path): + public = fixture_repertoire(('𝟠',), tuple(' A'), ucd_fixture(tmp_path)) + assert public['identities'].count('𝟠') == 1 + assert scalar('𝟠') == '𝟠' + + +def test_no_normalization(tmp_path): + public = fixture_repertoire(('é','e','\u0301'), tuple(' A'), ucd_fixture(tmp_path)) + assert all(c in public['identities'] for c in ('é','e','\u0301')) + + +def test_wrong_version_refuses(tmp_path): + root = ucd_fixture(tmp_path) + path = root / 'DerivedCoreProperties.txt' + path.write_text(path.read_text().replace(FIXTURE_VERSION, '17.0.0')) + with pytest.raises(ValueError, match='release'): + parse_fixture(root) + + +def test_missing_property_data_refuses(tmp_path): + root = ucd_fixture(tmp_path) + (root / 'UnicodeData.txt').write_text('') + with pytest.raises(ValueError, match='incomplete'): + parse_fixture(root) + + +def test_unclosed_unicode_range_refuses(tmp_path): + root = ucd_fixture(tmp_path) + with (root / 'UnicodeData.txt').open('a') as stream: + stream.write(';'.join(['3000', '', 'No'] + ['']*12) + '\n') + with pytest.raises(ValueError, match='unterminated'): + parse_fixture(root) + + +def test_invalid_scalar_and_anchor_refusal(tmp_path): + root = ucd_fixture(tmp_path) + for bad in ('', 'ab', '\ud800'): + with pytest.raises(ValueError): + fixture_repertoire((bad,), tuple(' A'), root) + for bad in (('A',), (' ', 'A', 'A')): + with pytest.raises(ValueError): + fixture_repertoire(('A',), bad, root) + + +def test_word_and_recursive_record(tmp_path): + factory = factory_fixture(tmp_path) + word = factory.word(1) + assert word.recover() == tuple('ABA') + assert [word.placement(i).radius() for i in range(3)] == [Fraction(1,8),Fraction(1,4),Fraction(1,2)] + obj = factory.record(1, 'definition', (('word',1),('character',' '),('word',1))) + assert ''.join(obj.recover()) == 'ABA ABA' + assert obj.participants[0].interior is obj.participants[2].interior is word + assert obj.placement(0).turn == factory.word_carrier.turn('ABA') + assert obj.placement(1).turn == factory.glyph_carrier.turn(' ') + higher = Carrier((obj.identity,), 'fixture-higher') + triple = affixiate('higher', (Participant.closed(higher,obj),), + origin='SPACE', relation='declared-nesting', source='fixture') + assert ''.join(triple.recover()) == 'ABA ABA' + + +def test_append_does_not_change_angle(tmp_path): + factory = factory_fixture(tmp_path) + before = factory.record(1,'definition',(('word',1),)) + after = factory.record(2,'definition',(('word',1),('word',2))) + assert after.placement(0).turn == before.placement(0).turn + assert after.placement(0).radius() == before.placement(0).radius()/2 + assert after.placement(1).radius() == Fraction(1,2) + + +def test_sql_materialization_and_independent_replay(tmp_path): + factory = factory_fixture(tmp_path) + chars = {i:c for i,c in enumerate(factory.glyph_carrier.identities)} + ids = {c:i for i,c in chars.items()} + receipts = {} + with sqlite3.connect(':memory:') as db: + db.executescript(SQL) + for wid, text in factory.words.items(): + refs = tuple(('character',ids[c]) for c in text) + obj = factory.word(wid) + _write_frame(db,'word',wid,obj,refs) + expected, positions = _independent_frame('word',wid,text,'ordered-glyphs', + factory.source_receipt,refs,factory,chars,receipts) + assert db.execute('SELECT * FROM frames WHERE kind=? AND id=?',('word',wid)).fetchone() == expected + assert db.execute('SELECT * FROM placements WHERE kind=? AND id=? ORDER BY ordinal',('word',wid)).fetchall() == positions + receipts[wid] = obj.receipt + refs = (('word',1),('character',ids[' ']),('word',2),('word',1)) + obj = factory.record(7,'definition',(('word',1),('character',' '),('word',2),('word',1))) + _write_frame(db,'record',7,obj,refs) + expected, positions = _independent_frame('record',7,'definition:7','ordered-definition', + factory.source_receipt,refs,factory,chars,receipts) + assert db.execute('SELECT * FROM frames WHERE kind=? AND id=?',('record',7)).fetchone() == expected + assert db.execute('SELECT * FROM placements WHERE kind=? AND id=? ORDER BY ordinal',('record',7)).fetchall() == positions + db.execute("UPDATE placements SET exponent=exponent+1 WHERE kind='record' AND ordinal=0") + assert db.execute('SELECT * FROM placements WHERE kind=? AND id=? ORDER BY ordinal',('record',7)).fetchall() != positions + + +def test_unknown_word_refuses(tmp_path): + factory = factory_fixture(tmp_path) + with pytest.raises(KeyError): + factory.word(999) + + +def test_geometry_producer_bytes_are_bound(): + _producer_check() + + +def test_fixture_cannot_claim_production_release(tmp_path): + root = ucd_fixture(tmp_path) + # Even the expected production header and minimum ASCII values are insufficient. + path = root / 'DerivedCoreProperties.txt' + path.write_text(path.read_text().replace(FIXTURE_VERSION, implementation.UNICODE_VERSION)) + with pytest.raises(ValueError, match='release hash mismatch'): + implementation.unicode_admission(root) + with pytest.raises(ValueError, match='release hash mismatch'): + implementation.repertoire(('A',), (' ', 'A'), root) + + +def test_fixture_layout_is_not_labelled_as_unicode_release(tmp_path): + assert factory_fixture(tmp_path).public['unicode_version'] == FIXTURE_VERSION + + +def manifest_fixture(): + from pathlib import Path + return { + 'schema': implementation.PROFILE, 'source_receipt': 'a' * 64, + 'source': {'fixture': True}, 'ucns_commit': implementation.UCNS_COMMIT, + 'implementation_sha256': implementation.file_hash(Path(implementation.__file__)), + 'public_sha256': 'b' * 64, 'glyph_carrier_receipt': 'c' * 64, + 'word_carrier_receipt': 'd' * 64, 'counts': {'frames': 1, 'placements': 1}, + 'geometry_receipt': 'e' * 64, 'character_definitions_receipt': 'f' * 64, + 'hmmm': list(implementation.MANIFEST_BOUNDARIES), + } + + +def test_manifest_shape_check_is_not_full_verification(): + # This checks only the schema. No source or geometry survival claim is emitted. + assert implementation._validate_manifest(manifest_fixture()) is None + + +@pytest.mark.parametrize('mutation', [ + lambda m: m.pop('hmmm'), + lambda m: m.update(hmmm=[]), + lambda m: m.update(hmmm=['everything is proven']), + lambda m: m.update(canonical=True), + lambda m: m.update(proof_status='SURVIVED'), + lambda m: m.update(counts={'frames': True, 'placements': 1}), + lambda m: m.update(counts={'frames': 1, 'placements': 1, 'extra': 1}), + lambda m: m.update(source_receipt='not a digest'), +]) +def test_modified_manifest_refuses_before_source_replay(tmp_path, mutation): + value = manifest_fixture() + mutation(value) + implementation._json_write(tmp_path / 'manifest.json', value) + with pytest.raises(ValueError, match='manifest'): + implementation.verify(tmp_path) + + +def test_nonobject_manifest_refuses(tmp_path): + implementation._json_write(tmp_path / 'manifest.json', []) + with pytest.raises(ValueError, match='manifest schema'): + implementation.verify(tmp_path) + + +def test_second_unicode_file_is_independently_hash_bound(tmp_path, monkeypatch): + from hashlib import sha256 + root = ucd_fixture(tmp_path) + # Allow only the first synthetic file, then prove the second still refuses. + monkeypatch.setitem(implementation.UCD_RELEASE_SHA256, 'UnicodeData.txt', + sha256((root / 'UnicodeData.txt').read_bytes()).hexdigest()) + with pytest.raises(ValueError, match='DerivedCoreProperties.txt'): + implementation.unicode_admission(root) + + +def test_unicode_read_refuses_oversize_before_open(tmp_path, monkeypatch): + path = tmp_path / 'oversize.txt' + with path.open('wb') as stream: + stream.truncate(implementation.UCD_MAX_BYTES + 1) + def forbidden_open(*args, **kwargs): + raise AssertionError('oversize file was opened') + monkeypatch.setattr(implementation.os, 'open', forbidden_open) + with pytest.raises(ValueError, match='acquisition budget'): + implementation._read_unicode_file(path) + + +@pytest.mark.parametrize('kind', ['symlink', 'directory', 'fifo']) +def test_unicode_read_refuses_nonregular_inputs(tmp_path, kind): + path = tmp_path / 'input' + if kind == 'symlink': + target = tmp_path / 'target' + target.write_bytes(b'not a release') + path.symlink_to(target) + elif kind == 'directory': + path.mkdir() + else: + if not hasattr(implementation.os, 'mkfifo'): + pytest.skip('platform has no FIFO filesystem objects') + implementation.os.mkfifo(path) + with pytest.raises(ValueError, match='regular nonsymlink'): + implementation._read_unicode_file(path) + + +def test_unicode_stream_read_is_capped_even_when_file_grows(tmp_path, monkeypatch): + from contextlib import contextmanager + path = tmp_path / 'growing.txt' + path.write_bytes(b'x') + monkeypatch.setattr(implementation, 'UCD_MAX_BYTES', 32) + fdopen = implementation.os.fdopen + requested = [] + @contextmanager + def tracked_open(fd, mode): + with fdopen(fd, mode) as stream: + class Tracked: + def fileno(self): + return stream.fileno() + def read(self, count): + requested.append(count) + with path.open('ab') as writer: + writer.write(b'x' * 64) + return stream.read(count) + yield Tracked() + monkeypatch.setattr(implementation.os, 'fdopen', tracked_open) + with pytest.raises(ValueError, match='acquisition budget'): + implementation._read_unicode_file(path) + assert requested == [33] + + +def test_unicode_input_replacement_during_open_refuses(tmp_path, monkeypatch): + path = tmp_path / 'swapped.txt' + path.write_bytes(b'first') + replacement = tmp_path / 'replacement' + replacement.write_bytes(b'second') + original_open = implementation.os.open + def replacing_open(name, flags): + replacement.replace(path) + return original_open(name, flags) + monkeypatch.setattr(implementation.os, 'open', replacing_open) + with pytest.raises(ValueError, match='changed'): + implementation._read_unicode_file(path) + + +def test_bounded_unicode_snapshot_preserves_exact_bytes(tmp_path): + path = tmp_path / 'regular.txt' + path.write_bytes(b'exact\x00bytes\n') + assert implementation._read_unicode_file(path) == b'exact\x00bytes\n' + + +def test_build_refuses_oversize_ucd_before_source_or_copy(tmp_path): + path = tmp_path / 'ucd' + path.mkdir() + with (path / 'UnicodeData.txt').open('wb') as stream: + stream.truncate(implementation.UCD_MAX_BYTES + 1) + output = tmp_path / 'must-not-be-created' / 'output' + with pytest.raises(ValueError, match='acquisition budget'): + implementation.build(tmp_path / 'missing-admission', path, output) + assert not output.parent.exists() + + +@pytest.mark.parametrize('kind', ['symlink', 'directory', 'fifo']) +def test_json_read_refuses_nonregular_inputs(tmp_path, kind): + path = tmp_path / 'manifest.json' + if kind == 'symlink': + target = tmp_path / 'target.json' + target.write_bytes(b'{}\n') + path.symlink_to(target) + elif kind == 'directory': + path.mkdir() + else: + if not hasattr(implementation.os, 'mkfifo'): + pytest.skip('platform has no FIFO filesystem objects') + implementation.os.mkfifo(path) + with pytest.raises(ValueError, match='regular nonsymlink'): + implementation._json_read(path) + + +def test_json_read_refuses_oversize_before_open(tmp_path, monkeypatch): + path = tmp_path / 'manifest.json' + with path.open('wb') as stream: + stream.truncate(65) + monkeypatch.setattr(implementation, 'JSON_MAX_BYTES', 64) + def forbidden_open(*args, **kwargs): + raise AssertionError('oversize JSON artifact was opened') + monkeypatch.setattr(implementation.os, 'open', forbidden_open) + with pytest.raises(ValueError, match='read budget'): + implementation._json_read(path) + + +def test_json_stream_read_is_capped_if_file_grows(tmp_path, monkeypatch): + from contextlib import contextmanager + path = tmp_path / 'manifest.json' + path.write_bytes(b'{}\n') + monkeypatch.setattr(implementation, 'JSON_MAX_BYTES', 32) + fdopen = implementation.os.fdopen + requested = [] + @contextmanager + def tracked_open(fd, mode): + with fdopen(fd, mode) as stream: + class Tracked: + def fileno(self): + return stream.fileno() + def read(self, count): + requested.append(count) + with path.open('ab') as writer: + writer.write(b'x' * 64) + return stream.read(count) + yield Tracked() + monkeypatch.setattr(implementation.os, 'fdopen', tracked_open) + with pytest.raises(ValueError, match='read budget'): + implementation._json_read(path) + assert requested == [33] diff --git a/research/english-gonol/tests/test_ordinal_definitions.py b/research/english-gonol/tests/test_ordinal_definitions.py new file mode 100644 index 00000000..ddcbf63a --- /dev/null +++ b/research/english-gonol/tests/test_ordinal_definitions.py @@ -0,0 +1,158 @@ +"""Run with the pinned UCNS: pytest -q tests/test_ordinal_definitions.py. + +These adversarial fixtures verify inheritance and placement, not full-corpus +admission. The hosted complete build remains the full-source witness. +""" +# === CHECKS === +# id: inherited_definitions_match_prior_source +# proves: ordinal_inherited_definition_preservation +# call: self::test_inherited_entries_and_behavior_are_unchanged +# id: definition_geometry_has_no_historical_fallback +# proves: ordinal_definition_replay +# call: self::test_no_old_constructor_and_independent_checker +# id: definition_corruption_is_rejected +# proves: ordinal_definition_replay +# call: self::test_tampering_refuses +# === END CHECKS === +from copy import deepcopy +from dataclasses import asdict +from fractions import Fraction +import json +from types import SimpleNamespace + +import pytest +from english_gonol import ordinal_definitions as definitions +from english_gonol.ordinal_construct import Factory +from english_gonol.language import character_definitions as prior + + +@pytest.fixture +def factory(): + names = ('zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine') + public = {'identities': list(prior.PUBLIC_GONOL_157) + ['𝟠'], 'fixture': True} + return Factory(public, dict(enumerate(names)), 'fixture-source') + + +def test_inherited_entries_and_behavior_are_unchanged(factory): + payload = definitions.build_definitions(factory) + layer = definitions.verify_definitions(factory, payload) + old = prior.load_character_definition_table() + assert len(layer.entries) == 158 + assert list(layer.entries) == list(factory.glyph_carrier.identities) + for char, entry in old.items(): + assert asdict(layer.entries[char]) == asdict(entry) + assert layer.entries['-'].classes == ('operator', 'punctuation') + assert layer.entries['y'].classes == ('vowel', 'consonant') + assert layer.entries['3'].number_name == 'three' + assert layer.entries['A'].hmmm and layer.entries['𝟠'].hmmm + for final in ('y', 'Y', 'a'): + for preceding in ('r', 'a', None, '𝟠'): + for suffix, initial in (('ing', True), ('ed', True), ('s', False)): + expected = prior.CharacterDefinitionLayer.final_y_realization( + SimpleNamespace(entries=old), final, preceding, suffix, initial) + assert layer.final_y_realization(final, preceding, suffix, initial) == expected + with pytest.raises(TypeError): + layer.entries['x'] = layer.entries['y'] + + +def test_relation_plan_matches_previous_builder(factory, monkeypatch): + calls = [] + def capture(**kwargs): + calls.append(kwargs) + return SimpleNamespace(gonol=SimpleNamespace(receipt_digest=kwargs['source_id']), + receipt_digest=kwargs['source_id']) + monkeypatch.setattr(prior, 'construct_gonol', capture) + prior.build_character_definition_layer() + observed = {c['source_id']: c.get('relation') for c in calls if c['scale'] == 'definition'} + payload = definitions.build_definitions(factory) + reconstructed = {d['source_id']: d['construction']['relation'] + for d in payload['definitions'] if d['owner'] in prior.PUBLIC_GONOL_157} + assert reconstructed == observed + + +def test_no_old_constructor_and_independent_checker(factory, monkeypatch): + def forbidden(*args, **kwargs): + raise AssertionError('forbidden constructor called') + monkeypatch.setattr(prior, 'construct_gonol', forbidden) + payload = definitions.build_definitions(factory) + monkeypatch.setattr(definitions, 'affixiate', forbidden) + monkeypatch.setattr(factory, 'word', forbidden) + definitions.verify_definitions(factory, payload) + + +def test_new_angles_and_recursive_number_name_recovery(factory): + payload = definitions.build_definitions(factory) + d = next(d for d in payload['definitions'] if d['source_id'] == 'char-def:3:number-name') + positions = d['construction']['placements'] + assert d['construction']['origin'] == 'SPACE' + assert [p['radius']['power_of_two_denominator'] for p in positions] == [2, 1] + assert positions[0]['arity'] == 158 + assert Fraction(*positions[0]['turn']) == factory.glyph_carrier.turn('3') + assert Fraction(*positions[1]['turn']) == factory.word_carrier.turn('three') + assert positions[0]['interior_receipt'] == payload['characters']['3']['receipt'] + assert positions[1]['interior_receipt'] == payload['number_names']['three']['receipt'] + word = payload['number_names']['three']['construction']['placements'] + assert [p['identity'] for p in word] == list('three') + assert [p['radius']['power_of_two_denominator'] for p in word] == [5, 4, 3, 2, 1] + assert payload['number_names']['three']['receipt'] == factory.word(factory.word_ids['three']).receipt + + +@pytest.mark.parametrize('mutate', [ + lambda p: p['entries'].pop(), + lambda p: p['entries'].reverse(), + lambda p: p['definitions'].pop(), + lambda p: p['definitions'][0]['construction']['placements'][0].update(arity=157), + lambda p: p['definitions'][0]['construction']['placements'][0]['radius'].update(power_of_two_denominator=2), + lambda p: p['definitions'][0]['construction']['placements'][0].update(interior_receipt=None), + lambda p: p['counts'].update(characters=True), + lambda p: p['characters']['A']['construction'].update(origin='A'), + lambda p: p['entries'][-1]['entry'].update(hmmm=False), + lambda p: p['source'].update(text='invented table'), + lambda p: p.update(hmmm=[]), +]) +def test_tampering_refuses(factory, mutate): + payload = definitions.build_definitions(factory) + mutate(payload) + with pytest.raises(ValueError, match='replay mismatch'): + definitions.verify_definitions(factory, payload) + + +def test_json_roundtrip_preserves_every_entry_and_closure(factory): + payload = definitions.build_definitions(factory) + restored = json.loads(json.dumps(payload, ensure_ascii=False)) + assert definitions.verify_definitions(factory, payload).receipt == definitions.verify_definitions(factory, restored).receipt + + +def test_missing_number_name_refuses_without_carrier_mutation(factory): + words = {i: word for i, word in factory.words.items() if word != 'three'} + narrowed = Factory(factory.public, words, factory.source_receipt) + before = narrowed.word_carrier.receipt + with pytest.raises(ValueError, match='not admitted: three'): + definitions.build_definitions(narrowed) + assert narrowed.word_carrier.receipt == before + + +def test_default_manifest_requires_definition_receipt(): + from english_gonol import ordinal_construct as implementation + from test_ordinal_construct import manifest_fixture + manifest = manifest_fixture() + assert implementation._validate_manifest(manifest) is None + manifest.pop('character_definitions_receipt') + with pytest.raises(ValueError, match='manifest schema'): + implementation._validate_manifest(manifest) + + +def test_default_build_and_verifier_consume_definition_component(): + import ast + import inspect + from english_gonol import ordinal_construct as implementation + producer = ast.parse(inspect.getsource(implementation.build)) + checker = ast.parse(inspect.getsource(implementation.verify)) + def names(tree): + return {n.func.id for n in ast.walk(tree) + if isinstance(n, ast.Call) and isinstance(n.func, ast.Name)} + assert 'build_definitions' in names(producer) + assert 'verify_definitions' in names(checker) + for tree in (producer, checker): + assert any(isinstance(n, ast.Constant) and n.value == 'character-definitions.json' + for n in ast.walk(tree)) diff --git a/research/english-gonol/tests/test_ordinal_source_inputs.py b/research/english-gonol/tests/test_ordinal_source_inputs.py new file mode 100644 index 00000000..c3eca767 --- /dev/null +++ b/research/english-gonol/tests/test_ordinal_source_inputs.py @@ -0,0 +1,55 @@ +"""Exercise the workflow's actual participant admission loop, without network.""" +import ast +from pathlib import Path +from types import SimpleNamespace +import pytest +import yaml + +# === CHECKS === +# id: ordinal_workflow_complete_input_mapping +# proves: english_expanded_repertoire +# call: self::test_participant_mapping_refuses_changes +# === END CHECKS === + + +def _program(): + root = Path(__file__).resolve().parents[3] + workflow = yaml.safe_load((root / '.github/workflows/english-ordinal-affixiation.yml').read_text()) + step = next(s for s in workflow['jobs']['full-corpus']['steps'] + if s.get('name') == 'Verify exact participant graph and unchanged source reader') + source = step['run'].split("<<'PY'\n", 1)[1].rsplit('\nPY', 1)[0] + tree = ast.parse(source) + first = next(i for i, n in enumerate(tree.body) if isinstance(n, ast.Assign) + and any(isinstance(t, ast.Name) and t.id == 'baseline_key' for t in n.targets)) + last = next(i for i, n in enumerate(tree.body) if isinstance(n, ast.Assign) + and any(isinstance(t, ast.Name) and t.id == 'active_base' for t in n.targets)) + return compile(ast.Module(body=tree.body[first:last], type_ignores=[]), '', 'exec') + + +def _run(change): + baseline = 'e3eb83764355b4b760a79323cda6a6d75c3a501a' + actual = 'a' * 40 + entries = [{'repository': 'fixture/repo', 'commit': actual}, + {'repository': 'The-Interdependency/stack', 'commit': baseline}] + change(entries) + def git(args, text): + return actual + '\n' if '-C' in args else baseline + '\n' + env = {'git_roots': {('fixture/repo', actual): 'fixture-checkout'}, + 'artifact_roots': {}, 'graph': {'repositories': entries}, + 'subprocess': SimpleNamespace(check_output=git)} + exec(_program(), env) + + +def test_participant_mapping_accepts_complete_inventory(): + _run(lambda rows: None) + + +@pytest.mark.parametrize('change', [ + lambda rows: rows.append({'repository': 'unmapped/repo', 'commit': 'b' * 40}), + lambda rows: rows[0].update(commit='b' * 40), + lambda rows: rows.pop(), + lambda rows: rows.append(dict(rows[0])), +]) +def test_participant_mapping_refuses_changes(change): + with pytest.raises(AssertionError): + _run(change) diff --git a/stack-manifest.json b/stack-manifest.json index a0616910..b7da5964 100644 --- a/stack-manifest.json +++ b/stack-manifest.json @@ -1,7 +1,7 @@ { "schema": "the-interdependency.stack-manifest", "version": "1.1.0", - "work_graph_sha256": "f4acf6c40732f486388ed43df896d6e21590f1bf567310a0f45814c56d506268", + "work_graph_sha256": "a28ff770ccb5c7cdae017272dd47e3bacb0b7054b07ebe7086fe26d534aaebb6", "repositories": [ { "repository": "The-Interdependency/skill-lib", @@ -73,7 +73,8 @@ "Weave has an explicit Stack-owned native binary sequence-cycle candidate with exact recovery; other profiles, private/public lifting, authentication/state and full-cipher security remain unresolved.", "Weave native public/private key derivation and source of asymmetry remain unresolved; source-bound key metadata does not establish a native asymmetric law.", "a0 Municipality is specification-first composition research; native semantic-origin/affixiation integration, operational authorities and its fifth workflow remain unresolved; no runtime or six-month proof is established by placement.", - "Pi radix research has measured finite cross-base recurrences; a base-independent segmentation or generating law remains unestablished, and no pi normality or cryptographic security follows from the finite prefixes." + "Pi radix research has measured finite cross-base recurrences; a base-independent segmentation or generating law remains unestablished, and no pi normality or cryptographic security follows from the finite prefixes.", + "English ordinal/1 is selected with inherited definitions reconstructed on its expanded base. SPACE attachment references are specified; global transport, vocabulary growth, source-uncovered definitions and downstream migration remain unresolved. Native Mobius integration is not required." ] }, "research_participants": [ @@ -472,6 +473,86 @@ "relation": "pinned doctrine for the module, tests, research receipts and Stack transaction; no authority transfer", "authority_transfer": false, "canonical_release": false + }, + { + "workspace": "research/english-gonol/", + "participant_id": "ordinal-source", + "repository": "The-Interdependency/stack", + "commit": "2ba7131c5931cccbd779ed08b59f8a2a356729d4", + "authority": "English construction and supported entrypoint", + "relation": "exact selected ordinal runtime source and supported entrypoint; no independent UCHC release authority", + "canonical_release": false, + "authority_transfer": false + }, + { + "workspace": "research/english-gonol/", + "participant_id": "ordinal-geometry", + "repository": "The-Interdependency/ucns", + "commit": "a7bfa0c96daca1f7445ba57c39fa6d8fd62e51eb", + "authority": "generic exact ordinal geometry", + "relation": "merged UCNS #241 producer; exact local radius and carrier angle law, not global transport or semantic authority", + "canonical_release": false, + "authority_transfer": false + }, + { + "workspace": "research/english-gonol/", + "participant_id": "ordinal-admission", + "repository": "The-Interdependency/stack", + "commit": "e3eb83764355b4b760a79323cda6a6d75c3a501a", + "authority": "English source admission and independent replay", + "relation": "unchanged thirteen-file source-reader closure; source admission is not the selected completed geometry", + "canonical_release": false, + "authority_transfer": false + }, + { + "workspace": "research/english-gonol/", + "participant_id": "ordinal-admission-carrier", + "repository": "The-Interdependency/ucns", + "commit": "4f863ad37096b7baab8f62820ad5cb937b62a3a7", + "authority": "historical carrier provenance", + "relation": "inherited scalar identity admission only; these historical angles do not supply the replacement placement", + "canonical_release": false, + "authority_transfer": false + }, + { + "workspace": "research/english-gonol/", + "participant_id": "ordinal-corpus", + "repository": "globalwordnet/english-wordnet", + "commit": "dc343f2683279ecbb13fab4e2fd778d7b162d287", + "authority": "complete English corpus evidence", + "relation": "all exact pinned source records consumed by the ordinal construction", + "canonical_release": false, + "authority_transfer": false + }, + { + "workspace": "research/english-gonol/", + "participant_id": "ordinal-unicode-data", + "repository": "unicode.org/Public/18.0.0/ucd/UnicodeData.txt", + "artifact_sha256": "0736451de439ae7baf1425136617da495e09ee5afbe6e394374db7009ea08950", + "authority": "Unicode 18.0.0 numeric-category source", + "relation": "exact immutable release file consumed by expanded public-gonol admission", + "canonical_release": false, + "authority_transfer": false + }, + { + "workspace": "research/english-gonol/", + "participant_id": "ordinal-unicode-math", + "repository": "unicode.org/Public/18.0.0/ucd/DerivedCoreProperties.txt", + "artifact_sha256": "09c928886a178fcafd93c29e4bd59073a058e5a100b716d425cb563ab50f68c9", + "authority": "Unicode 18.0.0 Math-property source", + "relation": "exact immutable release file consumed by expanded public-gonol admission", + "canonical_release": false, + "authority_transfer": false + }, + { + "workspace": "research/english-gonol/", + "participant_id": "ordinal-doctrine", + "repository": "The-Interdependency/skill-lib", + "commit": "e74ce1ee8a2cea50b0a6acce7697a6d3d9b2e128", + "authority": "build and evidence discipline", + "relation": "separately pinned ordinal work graph doctrine; root operational snapshot remains unchanged", + "canonical_release": false, + "authority_transfer": false } ] } diff --git a/tools/check_stack_consistency.py b/tools/check_stack_consistency.py index 1f1db150..ebd33447 100644 --- a/tools/check_stack_consistency.py +++ b/tools/check_stack_consistency.py @@ -120,9 +120,14 @@ def check_manifest_shape(manifest: dict[str, Any], findings: list[str]) -> None: if not HEX40.fullmatch(commit): error(findings, "repository.commit", f"{entry.get('repository')}: invalid commit {commit!r}") for entry in manifest.get("research_participants", []): - commit = entry.get("commit", "") - if not HEX40.fullmatch(commit): + commit = entry.get("commit") + artifact_sha256 = entry.get("artifact_sha256") + if (commit is None) == (artifact_sha256 is None): + error(findings, "research.identity", f"{entry.get('workspace')}:{entry.get('participant_id')}: exactly one of commit or artifact_sha256 is required") + elif commit is not None and not HEX40.fullmatch(str(commit)): error(findings, "research.commit", f"{entry.get('workspace')}:{entry.get('participant_id')}: invalid commit {commit!r}") + elif artifact_sha256 is not None and re.fullmatch(r"[0-9a-f]{64}", str(artifact_sha256)) is None: + error(findings, "research.artifact_sha256", f"{entry.get('workspace')}:{entry.get('participant_id')}: invalid artifact_sha256 {artifact_sha256!r}") if entry.get("authority_transfer") is not False: error(findings, "research.authority_transfer", f"{entry.get('workspace')}:{entry.get('participant_id')} must keep authority_transfer=false") @@ -166,15 +171,18 @@ def check_research_participants( participant_id = str(entry.get("participant_id", "")) repository = str(entry.get("repository", "")) commit = str(entry.get("commit", "")) + artifact_sha256 = str(entry.get("artifact_sha256", "")) key = (workspace, participant_id) if key in keys: error(findings, "research.duplicate", f"duplicate research participant {key!r}") keys.add(key) - sources.add((workspace, repository, commit)) + if commit: + sources.add((workspace, repository, commit)) if workspace and workspace not in human: error(findings, "research.human_drift", f"STACK_MANIFEST.md does not mention workspace {workspace!r}") - if commit and commit not in human: - error(findings, "research.human_drift", f"STACK_MANIFEST.md does not mention research commit {commit!r}") + identity = commit or artifact_sha256 + if identity and identity not in human: + error(findings, "research.human_drift", f"STACK_MANIFEST.md does not mention research identity {identity!r}") return keys, sources