xtalate 0.1.0.dev0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- xtalate-0.1.0.dev0/.claude/settings.local.json +39 -0
- xtalate-0.1.0.dev0/.github/workflows/ci.yml +48 -0
- xtalate-0.1.0.dev0/.gitignore +230 -0
- xtalate-0.1.0.dev0/.import_linter_cache/.gitignore +2 -0
- xtalate-0.1.0.dev0/.import_linter_cache/711309a5eafe379c50451bc7ebdb0a6d9c6d28e6.data.json +1 -0
- xtalate-0.1.0.dev0/.import_linter_cache/CACHEDIR.TAG +3 -0
- xtalate-0.1.0.dev0/.import_linter_cache/xtalate.meta.json +1 -0
- xtalate-0.1.0.dev0/CHANGELOG.md +47 -0
- xtalate-0.1.0.dev0/CITATION.cff +23 -0
- xtalate-0.1.0.dev0/CLAUDE.md +145 -0
- xtalate-0.1.0.dev0/LICENSE +201 -0
- xtalate-0.1.0.dev0/NOTICE +11 -0
- xtalate-0.1.0.dev0/PKG-INFO +161 -0
- xtalate-0.1.0.dev0/README.md +134 -0
- xtalate-0.1.0.dev0/docs/ARCHITECTURE_REVIEW.md +166 -0
- xtalate-0.1.0.dev0/docs/DECISIONS.md +292 -0
- xtalate-0.1.0.dev0/docs/IMPLEMENTATION_PLAN_v0.1.md +201 -0
- xtalate-0.1.0.dev0/docs/IMPLEMENTATION_PLAN_v0.2.md +161 -0
- xtalate-0.1.0.dev0/docs/IMPLEMENTATION_PLAN_v0.3.md +150 -0
- xtalate-0.1.0.dev0/docs/IMPLEMENTATION_PLAN_v0.4.md +128 -0
- xtalate-0.1.0.dev0/docs/IMPLEMENTATION_PLAN_v0.5.md +151 -0
- xtalate-0.1.0.dev0/docs/IMPLEMENTATION_PLAN_v0.6.md +137 -0
- xtalate-0.1.0.dev0/docs/IMPLEMENTATION_PLAN_v0.7.md +127 -0
- xtalate-0.1.0.dev0/docs/IMPLEMENTATION_PLAN_v1.0.md +127 -0
- xtalate-0.1.0.dev0/docs/Incremental_Roadmap_v1.0.md +214 -0
- xtalate-0.1.0.dev0/docs/MASTER_SPEC.md +3369 -0
- xtalate-0.1.0.dev0/examples/.gitkeep +0 -0
- xtalate-0.1.0.dev0/examples/convert_extxyz_to_poscar.py +92 -0
- xtalate-0.1.0.dev0/pyproject.toml +110 -0
- xtalate-0.1.0.dev0/src/xtalate/__init__.py +10 -0
- xtalate-0.1.0.dev0/src/xtalate/capabilities/__init__.py +19 -0
- xtalate-0.1.0.dev0/src/xtalate/capabilities/registry.py +126 -0
- xtalate-0.1.0.dev0/src/xtalate/cli/__init__.py +11 -0
- xtalate-0.1.0.dev0/src/xtalate/cli/main.py +431 -0
- xtalate-0.1.0.dev0/src/xtalate/cli/render.py +126 -0
- xtalate-0.1.0.dev0/src/xtalate/conversion/__init__.py +48 -0
- xtalate-0.1.0.dev0/src/xtalate/conversion/engine.py +560 -0
- xtalate-0.1.0.dev0/src/xtalate/conversion/preflight.py +127 -0
- xtalate-0.1.0.dev0/src/xtalate/conversion/report.py +74 -0
- xtalate-0.1.0.dev0/src/xtalate/discovery/__init__.py +28 -0
- xtalate-0.1.0.dev0/src/xtalate/discovery/engine.py +202 -0
- xtalate-0.1.0.dev0/src/xtalate/discovery/report.py +50 -0
- xtalate-0.1.0.dev0/src/xtalate/discovery/sniffer.py +95 -0
- xtalate-0.1.0.dev0/src/xtalate/exporters/__init__.py +34 -0
- xtalate-0.1.0.dev0/src/xtalate/exporters/extxyz.py +140 -0
- xtalate-0.1.0.dev0/src/xtalate/exporters/poscar.py +180 -0
- xtalate-0.1.0.dev0/src/xtalate/exporters/xyz.py +67 -0
- xtalate-0.1.0.dev0/src/xtalate/parsers/__init__.py +31 -0
- xtalate-0.1.0.dev0/src/xtalate/parsers/_common.py +57 -0
- xtalate-0.1.0.dev0/src/xtalate/parsers/extxyz.py +452 -0
- xtalate-0.1.0.dev0/src/xtalate/parsers/poscar.py +428 -0
- xtalate-0.1.0.dev0/src/xtalate/parsers/xyz.py +286 -0
- xtalate-0.1.0.dev0/src/xtalate/py.typed +0 -0
- xtalate-0.1.0.dev0/src/xtalate/recovery/__init__.py +41 -0
- xtalate-0.1.0.dev0/src/xtalate/recovery/engine.py +321 -0
- xtalate-0.1.0.dev0/src/xtalate/recovery/scenarios.py +86 -0
- xtalate-0.1.0.dev0/src/xtalate/registry.py +28 -0
- xtalate-0.1.0.dev0/src/xtalate/schema/__init__.py +40 -0
- xtalate-0.1.0.dev0/src/xtalate/schema/arrays.py +115 -0
- xtalate-0.1.0.dev0/src/xtalate/schema/elements.py +150 -0
- xtalate-0.1.0.dev0/src/xtalate/schema/models.py +283 -0
- xtalate-0.1.0.dev0/src/xtalate/schema/paths.py +88 -0
- xtalate-0.1.0.dev0/src/xtalate/schema/presence.py +152 -0
- xtalate-0.1.0.dev0/src/xtalate/sdk/__init__.py +27 -0
- xtalate-0.1.0.dev0/src/xtalate/sdk/capabilities.py +57 -0
- xtalate-0.1.0.dev0/src/xtalate/sdk/plugins.py +72 -0
- xtalate-0.1.0.dev0/src/xtalate/sdk/results.py +45 -0
- xtalate-0.1.0.dev0/src/xtalate/validation/__init__.py +25 -0
- xtalate-0.1.0.dev0/src/xtalate/validation/engine.py +585 -0
- xtalate-0.1.0.dev0/src/xtalate/validation/report.py +48 -0
- xtalate-0.1.0.dev0/src/xtalate/validation/rethreshold.py +122 -0
- xtalate-0.1.0.dev0/src/xtalate/validation/tolerance.py +125 -0
- xtalate-0.1.0.dev0/tests/__init__.py +0 -0
- xtalate-0.1.0.dev0/tests/_dummy_plugins.py +111 -0
- xtalate-0.1.0.dev0/tests/_format_helpers.py +54 -0
- xtalate-0.1.0.dev0/tests/capabilities/__init__.py +0 -0
- xtalate-0.1.0.dev0/tests/capabilities/test_registry.py +95 -0
- xtalate-0.1.0.dev0/tests/cli/__init__.py +0 -0
- xtalate-0.1.0.dev0/tests/cli/test_cli.py +220 -0
- xtalate-0.1.0.dev0/tests/conversion/__init__.py +0 -0
- xtalate-0.1.0.dev0/tests/conversion/test_engine.py +193 -0
- xtalate-0.1.0.dev0/tests/conversion/test_preflight.py +111 -0
- xtalate-0.1.0.dev0/tests/discovery/__init__.py +0 -0
- xtalate-0.1.0.dev0/tests/discovery/test_engine.py +136 -0
- xtalate-0.1.0.dev0/tests/discovery/test_sniffer.py +115 -0
- xtalate-0.1.0.dev0/tests/exporters/__init__.py +1 -0
- xtalate-0.1.0.dev0/tests/golden/.gitkeep +0 -0
- xtalate-0.1.0.dev0/tests/golden/extxyz/co-in-cell/expected.canonical.json +119 -0
- xtalate-0.1.0.dev0/tests/golden/extxyz/co-in-cell/manifest.yaml +21 -0
- xtalate-0.1.0.dev0/tests/golden/extxyz/co-in-cell/sample.extxyz +4 -0
- xtalate-0.1.0.dev0/tests/golden/poscar/nacl-primitive/POSCAR +10 -0
- xtalate-0.1.0.dev0/tests/golden/poscar/nacl-primitive/expected.canonical.json +115 -0
- xtalate-0.1.0.dev0/tests/golden/poscar/nacl-primitive/manifest.yaml +15 -0
- xtalate-0.1.0.dev0/tests/golden/schema/poscar_nacl.json +115 -0
- xtalate-0.1.0.dev0/tests/golden/schema/xyz_2frame_3atom.json +137 -0
- xtalate-0.1.0.dev0/tests/golden/xyz/water-traj/expected.canonical.json +137 -0
- xtalate-0.1.0.dev0/tests/golden/xyz/water-traj/manifest.yaml +14 -0
- xtalate-0.1.0.dev0/tests/golden/xyz/water-traj/water_traj.xyz +10 -0
- xtalate-0.1.0.dev0/tests/parsers/__init__.py +1 -0
- xtalate-0.1.0.dev0/tests/parsers/test_builtins_registration.py +82 -0
- xtalate-0.1.0.dev0/tests/parsers/test_extxyz.py +205 -0
- xtalate-0.1.0.dev0/tests/parsers/test_poscar.py +176 -0
- xtalate-0.1.0.dev0/tests/parsers/test_xyz.py +110 -0
- xtalate-0.1.0.dev0/tests/recovery/__init__.py +0 -0
- xtalate-0.1.0.dev0/tests/recovery/test_engine.py +175 -0
- xtalate-0.1.0.dev0/tests/recovery/test_scenarios.py +35 -0
- xtalate-0.1.0.dev0/tests/roundtrip/.gitkeep +0 -0
- xtalate-0.1.0.dev0/tests/roundtrip/__init__.py +1 -0
- xtalate-0.1.0.dev0/tests/roundtrip/test_identity.py +120 -0
- xtalate-0.1.0.dev0/tests/schema/__init__.py +0 -0
- xtalate-0.1.0.dev0/tests/schema/test_arrays.py +72 -0
- xtalate-0.1.0.dev0/tests/schema/test_golden_roundtrip.py +70 -0
- xtalate-0.1.0.dev0/tests/schema/test_models.py +158 -0
- xtalate-0.1.0.dev0/tests/schema/test_paths.py +60 -0
- xtalate-0.1.0.dev0/tests/schema/test_presence.py +115 -0
- xtalate-0.1.0.dev0/tests/sdk/__init__.py +0 -0
- xtalate-0.1.0.dev0/tests/sdk/test_contracts.py +129 -0
- xtalate-0.1.0.dev0/tests/test_smoke.py +38 -0
- xtalate-0.1.0.dev0/tests/validation/__init__.py +0 -0
- xtalate-0.1.0.dev0/tests/validation/test_engine.py +222 -0
- xtalate-0.1.0.dev0/tests/validation/test_rethreshold.py +77 -0
- xtalate-0.1.0.dev0/tests/validation/test_tolerance.py +56 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(grep -n '^#\\\\{1,3\\\\} ' /Users/ailsayilu/ChemBridge/docs/MASTER_SPEC.md)",
|
|
5
|
+
"Bash(python3 -m venv /tmp/cb-venv)",
|
|
6
|
+
"Bash(/tmp/cb-venv/bin/python -m pip install --upgrade pip -q)",
|
|
7
|
+
"Bash(/tmp/cb-venv/bin/pip install *)",
|
|
8
|
+
"Bash(/tmp/cb-venv/bin/ruff check *)",
|
|
9
|
+
"Bash(/tmp/cb-venv/bin/ruff format *)",
|
|
10
|
+
"Bash(/tmp/cb-venv/bin/mypy)",
|
|
11
|
+
"Bash(/tmp/cb-venv/bin/lint-imports)",
|
|
12
|
+
"Bash(/tmp/cb-venv/bin/pytest -q)",
|
|
13
|
+
"Bash(/tmp/cb-venv/bin/chembridge)",
|
|
14
|
+
"Bash(git checkout *)",
|
|
15
|
+
"Bash(git add *)",
|
|
16
|
+
"Bash(git commit *)",
|
|
17
|
+
"Bash(rm -rf /tmp/cb-venv && echo \"venv removed\" && git log --oneline -1 && git branch --show-current)",
|
|
18
|
+
"Bash(pip show *)",
|
|
19
|
+
"Bash(python3 -m venv .venv)",
|
|
20
|
+
"Bash(.venv/bin/python -m pip install --upgrade pip -q)",
|
|
21
|
+
"Bash(.venv/bin/pip install *)",
|
|
22
|
+
"Bash(.venv/bin/python --version)",
|
|
23
|
+
"Bash(.venv/bin/python -c \"import chembridge; print\\('chembridge', chembridge.__version__, 'importable from', chembridge.__file__\\)\")",
|
|
24
|
+
"Bash(.venv/bin/ruff check *)",
|
|
25
|
+
"Bash(.venv/bin/ruff format *)",
|
|
26
|
+
"Bash(.venv/bin/mypy)",
|
|
27
|
+
"Bash(.venv/bin/lint-imports)",
|
|
28
|
+
"Bash(.venv/bin/pytest -q)",
|
|
29
|
+
"Bash(.venv/bin/python -c \"import yaml,sys\")",
|
|
30
|
+
"Bash(.venv/bin/python -c \"import yaml; d=yaml.safe_load\\(open\\('.github/workflows/ci.yml'\\)\\); print\\(' matrix:', d['jobs']['lint-and-test']['strategy']['matrix']['python-version']\\)\")",
|
|
31
|
+
"Bash(python3 -c \"import yaml; d=yaml.safe_load\\(open\\('.github/workflows/ci.yml'\\)\\); print\\(' matrix:', d['jobs']['lint-and-test']['strategy']['matrix']['python-version']\\)\")",
|
|
32
|
+
"Bash(git config *)",
|
|
33
|
+
"Bash(echo \"name : $\\(git config user.name\\)\")",
|
|
34
|
+
"Bash(echo \"email: $\\(git config user.email\\)\")",
|
|
35
|
+
"Bash(git rebase *)",
|
|
36
|
+
"Bash(git fetch *)"
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches: [main]
|
|
7
|
+
|
|
8
|
+
concurrency:
|
|
9
|
+
group: ci-${{ github.ref }}
|
|
10
|
+
cancel-in-progress: true
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
lint-and-test:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
strategy:
|
|
16
|
+
fail-fast: false
|
|
17
|
+
matrix:
|
|
18
|
+
# 3.11 is the supported floor (catches use of newer syntax); 3.13 is the
|
|
19
|
+
# local dev interpreter (DECISIONS.md D11) — testing both keeps dev and CI
|
|
20
|
+
# from drifting.
|
|
21
|
+
python-version: ["3.11", "3.13"]
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
|
+
|
|
25
|
+
- uses: actions/setup-python@v5
|
|
26
|
+
with:
|
|
27
|
+
python-version: ${{ matrix.python-version }}
|
|
28
|
+
cache: pip
|
|
29
|
+
|
|
30
|
+
- name: Install
|
|
31
|
+
run: |
|
|
32
|
+
python -m pip install --upgrade pip
|
|
33
|
+
pip install -e ".[dev]"
|
|
34
|
+
|
|
35
|
+
- name: Ruff (lint)
|
|
36
|
+
run: ruff check .
|
|
37
|
+
|
|
38
|
+
- name: Ruff (format)
|
|
39
|
+
run: ruff format --check .
|
|
40
|
+
|
|
41
|
+
- name: Mypy
|
|
42
|
+
run: mypy
|
|
43
|
+
|
|
44
|
+
- name: Import-graph lint (P2)
|
|
45
|
+
run: lint-imports
|
|
46
|
+
|
|
47
|
+
- name: Pytest
|
|
48
|
+
run: pytest
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
# Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# UV
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
# uv.lock
|
|
102
|
+
|
|
103
|
+
# poetry
|
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
106
|
+
# commonly ignored for libraries.
|
|
107
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
108
|
+
# poetry.lock
|
|
109
|
+
# poetry.toml
|
|
110
|
+
|
|
111
|
+
# pdm
|
|
112
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
113
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
114
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
115
|
+
# pdm.lock
|
|
116
|
+
# pdm.toml
|
|
117
|
+
.pdm-python
|
|
118
|
+
.pdm-build/
|
|
119
|
+
|
|
120
|
+
# pixi
|
|
121
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
122
|
+
# pixi.lock
|
|
123
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
124
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
125
|
+
.pixi
|
|
126
|
+
|
|
127
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
128
|
+
__pypackages__/
|
|
129
|
+
|
|
130
|
+
# Celery stuff
|
|
131
|
+
celerybeat-schedule
|
|
132
|
+
celerybeat.pid
|
|
133
|
+
|
|
134
|
+
# Redis
|
|
135
|
+
*.rdb
|
|
136
|
+
*.aof
|
|
137
|
+
*.pid
|
|
138
|
+
|
|
139
|
+
# RabbitMQ
|
|
140
|
+
mnesia/
|
|
141
|
+
rabbitmq/
|
|
142
|
+
rabbitmq-data/
|
|
143
|
+
|
|
144
|
+
# ActiveMQ
|
|
145
|
+
activemq-data/
|
|
146
|
+
|
|
147
|
+
# SageMath parsed files
|
|
148
|
+
*.sage.py
|
|
149
|
+
|
|
150
|
+
# Environments
|
|
151
|
+
.env
|
|
152
|
+
.envrc
|
|
153
|
+
.venv
|
|
154
|
+
env/
|
|
155
|
+
venv/
|
|
156
|
+
ENV/
|
|
157
|
+
env.bak/
|
|
158
|
+
venv.bak/
|
|
159
|
+
|
|
160
|
+
# Spyder project settings
|
|
161
|
+
.spyderproject
|
|
162
|
+
.spyproject
|
|
163
|
+
|
|
164
|
+
# Rope project settings
|
|
165
|
+
.ropeproject
|
|
166
|
+
|
|
167
|
+
# mkdocs documentation
|
|
168
|
+
/site
|
|
169
|
+
|
|
170
|
+
# mypy
|
|
171
|
+
.mypy_cache/
|
|
172
|
+
.dmypy.json
|
|
173
|
+
dmypy.json
|
|
174
|
+
|
|
175
|
+
# Pyre type checker
|
|
176
|
+
.pyre/
|
|
177
|
+
|
|
178
|
+
# pytype static type analyzer
|
|
179
|
+
.pytype/
|
|
180
|
+
|
|
181
|
+
# Cython debug symbols
|
|
182
|
+
cython_debug/
|
|
183
|
+
|
|
184
|
+
# PyCharm
|
|
185
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
186
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
187
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
188
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
189
|
+
# .idea/
|
|
190
|
+
|
|
191
|
+
# Abstra
|
|
192
|
+
# Abstra is an AI-powered process automation framework.
|
|
193
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
194
|
+
# Learn more at https://abstra.io/docs
|
|
195
|
+
.abstra/
|
|
196
|
+
|
|
197
|
+
# Visual Studio Code
|
|
198
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
199
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
200
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
201
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
202
|
+
# .vscode/
|
|
203
|
+
# Temporary file for partial code execution
|
|
204
|
+
tempCodeRunnerFile.py
|
|
205
|
+
|
|
206
|
+
# Ruff stuff:
|
|
207
|
+
.ruff_cache/
|
|
208
|
+
|
|
209
|
+
# PyPI configuration file
|
|
210
|
+
.pypirc
|
|
211
|
+
|
|
212
|
+
# Marimo
|
|
213
|
+
marimo/_static/
|
|
214
|
+
marimo/_lsp/
|
|
215
|
+
__marimo__/
|
|
216
|
+
|
|
217
|
+
# Streamlit
|
|
218
|
+
.streamlit/secrets.toml
|
|
219
|
+
|
|
220
|
+
#MacOS
|
|
221
|
+
.DS_Store
|
|
222
|
+
|
|
223
|
+
# Secrets (CLAUDE.md "Never commit secrets" / DECISIONS.md) — defensive, ahead of need:
|
|
224
|
+
# no credentials exist in the v0.1 codebase yet, but these patterns are excluded before
|
|
225
|
+
# the v0.5 Service layer (DB, object storage, hosted-instance API keys) makes them real.
|
|
226
|
+
*.pem
|
|
227
|
+
*.key
|
|
228
|
+
credentials.json
|
|
229
|
+
secrets.*
|
|
230
|
+
!secrets.*.example
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"xtalate.parsers":[["xtalate.parsers.extxyz",14,"from xtalate.parsers.extxyz import ExtxyzParser"],["xtalate.sdk",17,"from xtalate.sdk import ParserPlugin"],["xtalate.parsers.poscar",15,"from xtalate.parsers.poscar import PoscarParser, make_contcar_parser, make_poscar_parser"],["xtalate.parsers.xyz",16,"from xtalate.parsers.xyz import XyzParser"]],"xtalate.parsers._common":[["xtalate.schema",15,"from xtalate.schema import ConversionRecord, Provenance"],["xtalate",14,"from xtalate import __version__"]],"xtalate.recovery.scenarios":[],"xtalate.recovery":[["xtalate.recovery.engine",15,"from xtalate.recovery.engine import ("],["xtalate.recovery.scenarios",23,"from xtalate.recovery.scenarios import ("]],"xtalate.exporters.poscar":[["xtalate.schema",21,"from xtalate.schema import CanonicalObject"],["xtalate.sdk",22,"from xtalate.sdk import ("]],"xtalate.validation":[["xtalate.validation.engine",13,"from xtalate.validation.engine import ConversionReportView, ValidationEngine"],["xtalate.validation.report",14,"from xtalate.validation.report import CheckResult, ValidationReport"],["xtalate.validation.tolerance",16,"from xtalate.validation.tolerance import ToleranceProfile"],["xtalate.validation.rethreshold",15,"from xtalate.validation.rethreshold import rethreshold"]],"xtalate.validation.tolerance":[],"xtalate.discovery.sniffer":[["xtalate.capabilities",26,"from xtalate.capabilities import Registry"]],"xtalate.schema.presence":[["xtalate.schema.models",30,"from xtalate.schema.models import CanonicalObject, Frame"]],"xtalate.capabilities":[["xtalate.capabilities.registry",9,"from xtalate.capabilities.registry import ("]],"xtalate.discovery.engine":[["xtalate.sdk",25,"from xtalate.sdk import CapabilityLevel, ParseError, ParseIssue"],["xtalate.capabilities",21,"from xtalate.capabilities import Registry"],["xtalate.schema",24,"from xtalate.schema import CanonicalObject"],["xtalate.discovery.sniffer",23,"from xtalate.discovery.sniffer import Sniffer, SniffResult"],["xtalate.discovery.report",22,"from xtalate.discovery.report import DiscoveryReport, FieldPresenceEntry"]],"xtalate":[],"xtalate.schema":[["xtalate.schema.models",7,"from xtalate.schema.models import ("],["xtalate.schema.presence",22,"from xtalate.schema.presence import PathPresence, PresenceMap"]],"xtalate.exporters":[["xtalate.exporters.poscar",14,"from xtalate.exporters.poscar import ("],["xtalate.exporters.extxyz",13,"from xtalate.exporters.extxyz import ExtxyzExporter"],["xtalate.exporters.xyz",19,"from xtalate.exporters.xyz import XyzExporter"],["xtalate.sdk",20,"from xtalate.sdk import ExporterPlugin"]],"xtalate.schema.arrays":[],"xtalate.conversion.report":[],"xtalate.sdk":[["xtalate.sdk.plugins",15,"from xtalate.sdk.plugins import ExporterPlugin, ParserPlugin"],["xtalate.sdk.results",16,"from xtalate.sdk.results import ParseError, ParseIssue, ParseResult"],["xtalate.sdk.capabilities",10,"from xtalate.sdk.capabilities import ("]],"xtalate.parsers.poscar":[["xtalate.schema",26,"from xtalate.schema import ("],["xtalate.parsers._common",25,"from xtalate.parsers._common import build_provenance"],["xtalate.schema.elements",36,"from xtalate.schema.elements import is_valid_symbol"],["xtalate.sdk",37,"from xtalate.sdk import ("]],"xtalate.validation.report":[["xtalate.sdk",15,"from xtalate.sdk import ParseIssue"]],"xtalate.cli.render":[["xtalate.discovery.report",12,"from xtalate.discovery.report import DiscoveryReport"],["xtalate.sdk",13,"from xtalate.sdk import CapabilityLevel, FormatCapabilities"],["xtalate.conversion.report",11,"from xtalate.conversion.report import ConversionReport"],["xtalate.validation.report",14,"from xtalate.validation.report import ValidationReport"]],"xtalate.discovery.report":[["xtalate.sdk",22,"from xtalate.sdk import CapabilityLevel, ParseIssue"]],"xtalate.sdk.capabilities":[],"xtalate.schema.elements":[],"xtalate.discovery":[["xtalate.discovery.report",10,"from xtalate.discovery.report import DiscoveryReport, FieldPresenceEntry"],["xtalate.discovery.sniffer",11,"from xtalate.discovery.sniffer import ("],["xtalate.discovery.engine",9,"from xtalate.discovery.engine import DiscoveryEngine"]],"xtalate.validation.engine":[["xtalate.schema",32,"from xtalate.schema import CanonicalObject"],["xtalate.capabilities",31,"from xtalate.capabilities import Registry"],["xtalate.sdk",33,"from xtalate.sdk import ParseError, ParseIssue"],["xtalate.validation.tolerance",35,"from xtalate.validation.tolerance import ToleranceProfile"],["xtalate.validation.report",34,"from xtalate.validation.report import CheckResult, ValidationReport"]],"xtalate.cli.main":[["xtalate.discovery",32,"from xtalate.discovery import DiscoveryEngine"],["xtalate.capabilities",24,"from xtalate.capabilities import Registry"],["xtalate.sdk",34,"from xtalate.sdk import ParseError"],["xtalate.registry",33,"from xtalate.registry import default_registry"],["xtalate.validation.rethreshold",35,"from xtalate.validation import ("],["xtalate.discovery",253,"from xtalate.discovery import Sniffer"],["xtalate.sdk",273,"from xtalate.sdk import ParseIssue"],["xtalate.cli.render",25,"from xtalate.cli import render"],["xtalate.validation",35,"from xtalate.validation import ("],["xtalate",23,"from xtalate import __version__"],["xtalate.conversion",26,"from xtalate.conversion import ("]],"xtalate.sdk.plugins":[["xtalate.sdk.results",17,"from xtalate.sdk.results import ParseResult"],["xtalate.schema",15,"from xtalate.schema import CanonicalObject"],["xtalate.sdk.capabilities",16,"from xtalate.sdk.capabilities import FormatCapabilities"]],"xtalate.parsers.extxyz":[["xtalate.parsers._common",47,"from xtalate.parsers._common import build_provenance"],["xtalate.schema",48,"from xtalate.schema import ("],["xtalate.sdk",58,"from xtalate.sdk import ("]],"xtalate.registry":[["xtalate.parsers",18,"from xtalate.parsers import builtin_parsers"],["xtalate.capabilities",16,"from xtalate.capabilities import Registry"],["xtalate.exporters",17,"from xtalate.exporters import builtin_exporters"]],"xtalate.conversion.preflight":[["xtalate.recovery",28,"from xtalate.recovery import UnresolvedScenario"],["xtalate.capabilities",26,"from xtalate.capabilities import CapabilityMatrix"],["xtalate.sdk",30,"from xtalate.sdk import CapabilityLevel"],["xtalate.schema",29,"from xtalate.schema import CanonicalObject"],["xtalate.conversion.report",27,"from xtalate.conversion.report import PreservedEntry, RemovedEntry, ReportWarning"]],"xtalate.recovery.engine":[["xtalate.recovery.scenarios",28,"from xtalate.recovery.scenarios import ("],["xtalate.schema",33,"from xtalate.schema import AtomsBlock, CanonicalObject, Cell, Frame"]],"xtalate.sdk.results":[["xtalate.schema",15,"from xtalate.schema import CanonicalObject"]],"xtalate.exporters.xyz":[["xtalate.schema",16,"from xtalate.schema import CanonicalObject"],["xtalate.sdk",17,"from xtalate.sdk import ("]],"xtalate.schema.paths":[["xtalate.schema.models",19,"from xtalate.schema.models import ("]],"xtalate.capabilities.registry":[["xtalate.schema.paths",16,"from xtalate.schema.paths import expand_capability_path, is_valid_path"],["xtalate.sdk",17,"from xtalate.sdk import ("]],"xtalate.parsers.xyz":[["xtalate.schema",30,"from xtalate.schema import ("],["xtalate.sdk",38,"from xtalate.sdk import ("],["xtalate.parsers._common",29,"from xtalate.parsers._common import build_provenance"],["xtalate.schema.elements",37,"from xtalate.schema.elements import is_valid_symbol"]],"xtalate.exporters.extxyz":[["xtalate.schema",22,"from xtalate.schema import CanonicalObject, Frame"],["xtalate.sdk",23,"from xtalate.sdk import ("]],"xtalate.conversion.engine":[["xtalate.sdk",58,"from xtalate.sdk import ParseIssue"],["xtalate",36,"from xtalate import __version__"],["xtalate.validation",59,"from xtalate.validation import ToleranceProfile, ValidationEngine, ValidationReport"],["xtalate.conversion.preflight",38,"from xtalate.conversion.preflight import PreflightDiff, build_preflight"],["xtalate.recovery",45,"from xtalate.recovery import RecoveryEngine"],["xtalate.capabilities",37,"from xtalate.capabilities import Registry"],["xtalate.conversion.report",383,"from xtalate.conversion.report import ReportWarning"],["xtalate.conversion.report",39,"from xtalate.conversion.report import ("],["xtalate.schema",46,"from xtalate.schema import ("]],"xtalate.schema.models":[["xtalate.schema.arrays",26,"from xtalate.schema.arrays import ("],["xtalate.schema.presence",248,"from xtalate.schema.presence import compute_field_presence"],["xtalate.schema.elements",33,"from xtalate.schema.elements import atomic_number, is_valid_symbol"],["xtalate.schema.presence",24,"from xtalate.schema.presence import PresenceMap"]],"xtalate.validation.rethreshold":[["xtalate.validation.tolerance",21,"from xtalate.validation.tolerance import ToleranceProfile"],["xtalate.validation.report",20,"from xtalate.validation.report import CheckResult, ValidationReport"]],"xtalate.conversion":[["xtalate.conversion.preflight",18,"from xtalate.conversion.preflight import ("],["xtalate.conversion.engine",12,"from xtalate.conversion.engine import ("],["xtalate.recovery",31,"from xtalate.recovery import UnresolvedScenario"],["xtalate.conversion.report",23,"from xtalate.conversion.report import ("]],"xtalate.cli":[["xtalate.cli.main",9,"from xtalate.cli.main import main"]]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"xtalate.schema.presence": 1783833043.4318962, "xtalate.schema.arrays": 1783528075.2865715, "xtalate.schema.elements": 1783528075.2868724, "xtalate.exporters.xyz": 1783833043.4248168, "xtalate.sdk.plugins": 1783833043.4329252, "xtalate": 1783833043.4171271, "xtalate.conversion": 1783833043.4197884, "xtalate.discovery": 1783833043.4215312, "xtalate.cli.render": 1783833043.4192166, "xtalate.discovery.report": 1783833043.4224696, "xtalate.validation": 1783833043.4333227, "xtalate.validation.report": 1783833043.4348226, "xtalate.recovery": 1783833043.428564, "xtalate.exporters.poscar": 1783833043.4242523, "xtalate.recovery.scenarios": 1783833043.4294102, "xtalate.conversion.preflight": 1783833043.4210417, "xtalate.conversion.report": 1783830156.67924, "xtalate.parsers.xyz": 1783833043.427966, "xtalate.sdk.results": 1783833043.4331102, "xtalate.validation.engine": 1783833043.4338317, "xtalate.sdk.capabilities": 1783833043.4327276, "xtalate.parsers._common": 1783833043.4252386, "xtalate.schema": 1783833043.4300969, "xtalate.sdk": 1783833043.4324012, "xtalate.exporters": 1783833043.4230797, "xtalate.validation.tolerance": 1783830156.685212, "xtalate.capabilities.registry": 1783833043.4178298, "xtalate.capabilities": 1783833043.4173203, "xtalate.cli": 1783833043.4184995, "xtalate.exporters.extxyz": 1783833043.423647, "xtalate.schema.paths": 1783833043.431377, "xtalate.registry": 1783833043.429924, "xtalate.parsers.extxyz": 1783833043.4258244, "xtalate.parsers": 1783833043.4250007, "xtalate.recovery.engine": 1783833043.428934, "xtalate.discovery.engine": 1783833043.4218535, "xtalate.conversion.engine": 1783833043.4202921, "xtalate.discovery.sniffer": 1783833043.4227653, "xtalate.parsers.poscar": 1783833043.4269419, "xtalate.cli.main": 1783833043.4189324, "xtalate.validation.rethreshold": 1783833043.435106, "xtalate.schema.models": 1783833043.4308712}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to Xtalate are recorded here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project adheres to
|
|
5
|
+
[Semantic Versioning](https://semver.org/spec/v2.0.0.html). The canonical schema version is
|
|
6
|
+
tracked separately from the package version and reaches `1.0.0` only in the v1.0 release
|
|
7
|
+
(`docs/MASTER_SPEC.md` Part 2 §5); v0.1 objects carry `schema_version = "0.1.0"`.
|
|
8
|
+
|
|
9
|
+
## [0.1.0] — 2026-07-10
|
|
10
|
+
|
|
11
|
+
First release: the complete pure-Python **library + CLI** core. It converts between four
|
|
12
|
+
computational-chemistry formats while reporting — and independently re-validating — every
|
|
13
|
+
byte of scientific information kept, dropped, or fabricated.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- **Canonical Data Model** (`xtalate.schema`) — the single internal schema every parser
|
|
18
|
+
writes and every exporter reads, with the normative absence convention (`None` = "never in
|
|
19
|
+
the source" vs a real zero) and an on-demand `field_presence()` introspection.
|
|
20
|
+
- **Plugin SDK, Format Sniffer, and Capability Matrix** (`xtalate.sdk`,
|
|
21
|
+
`xtalate.discovery`, `xtalate.capabilities`) — stable parser/exporter contracts, a
|
|
22
|
+
generic confidence-scored sniffer, and a per-format, per-field read/write capability registry.
|
|
23
|
+
- **Formats** (`xtalate.parsers`, `xtalate.exporters`) — read and write for plain **XYZ**,
|
|
24
|
+
**extended XYZ** (ASE-backed, with default-laundering), **POSCAR**, and **CONTCAR**, each with a
|
|
25
|
+
golden round-trip and error-fixture suite.
|
|
26
|
+
- **Information Discovery Engine** (`xtalate.discovery`) — the ✓/✗ Discovery Report: a file's
|
|
27
|
+
canonical-field inventory annotated with the detected format's read capability.
|
|
28
|
+
- **Conversion Engine** (`xtalate.conversion`) — the pre-flight capability diff, the
|
|
29
|
+
`write_plan` discipline (materialized as a filtered `canonical′`), the Conversion Report, and
|
|
30
|
+
the completeness invariant enforced as an always-on runtime assertion.
|
|
31
|
+
- **Recovery Engine** (`xtalate.recovery`) — explicit, preset-only resolution of the
|
|
32
|
+
`frame_selection` and `missing_lattice` scenarios under the three-way hazard model; every
|
|
33
|
+
choice recorded as an Assumption, and a structured **refusal** when no choice is supplied.
|
|
34
|
+
- **Validation Engine** (`xtalate.validation`) — the unconditional post-conversion re-parse
|
|
35
|
+
and nine-check diff (Part 5 §2) under named tolerance profiles (`default`/`strict`/`loose`),
|
|
36
|
+
plus stored-report re-thresholding.
|
|
37
|
+
- **CLI** (`xtalate`) — `inspect`, `convert`, `validate`, and `capabilities`, with the
|
|
38
|
+
CI-native exit-code contract (`0`/`2`/`3`/`4`/`5`/`1`) and `--json` structured output.
|
|
39
|
+
|
|
40
|
+
### Known limitations (v0.1 scope)
|
|
41
|
+
|
|
42
|
+
- No web service, REST API, or UI (v0.5 / v0.6).
|
|
43
|
+
- CIF, XDATCAR, and ASE `.traj` — the remaining Phase-1 formats — are not yet implemented (v0.2+).
|
|
44
|
+
- Recovery is preset-only; tolerance profiles are the three named ones (custom tables are later
|
|
45
|
+
seams).
|
|
46
|
+
|
|
47
|
+
[0.1.0]: https://github.com/jsong1218/Xtalate/releases/tag/v0.1.0
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "If you use Xtalate in your research, please cite it as below."
|
|
3
|
+
title: "Xtalate: an audit-first converter for computational-chemistry file formats"
|
|
4
|
+
abstract: >-
|
|
5
|
+
Xtalate is a translation layer between computational-chemistry file formats that reports
|
|
6
|
+
exactly what each conversion keeps, loses, or fabricates, and independently re-validates the
|
|
7
|
+
result. All translation flows through a single canonical data model; loss is predicted before
|
|
8
|
+
conversion and verified after.
|
|
9
|
+
type: software
|
|
10
|
+
authors:
|
|
11
|
+
- family-names: Song
|
|
12
|
+
given-names: Jiayu
|
|
13
|
+
version: 0.1.0
|
|
14
|
+
date-released: 2026-07-10
|
|
15
|
+
license: Apache-2.0
|
|
16
|
+
repository-code: "https://github.com/jsong1218/Xtalate"
|
|
17
|
+
keywords:
|
|
18
|
+
- computational chemistry
|
|
19
|
+
- file format conversion
|
|
20
|
+
- materials science
|
|
21
|
+
- data provenance
|
|
22
|
+
- VASP
|
|
23
|
+
- ASE
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# CLAUDE.md — Xtalate Project Context
|
|
2
|
+
|
|
3
|
+
> Load this file at the start of every Claude Code / Claude chat session working on Xtalate. It is a compressed index of `docs/MASTER_SPEC.md` (the full constitution — a single edited document, *not* an assembly of standalone files; see the document-family section below). If anything here conflicts with a doc in `docs/`, **the doc wins** — this file is a map, not the territory. If a later-uploaded doc contradicts an earlier one, flag the discrepancy; do not silently pick one.
|
|
4
|
+
|
|
5
|
+
> **Status (refreshed Revision 1.6, July 2026).** **v0.1 is feature-complete** (MASTER_SPEC Preface Revision 1.2 item 17): the full spine — parse → pre-flight → recovery → export → report → validation — plus the Information Discovery Engine and the `xtalate` CLI are implemented, tested, and shipped for the four v0.1 formats (XYZ, extXYZ, POSCAR, CONTCAR). The map below is now current against Revisions 1.3–1.6 and `docs/DECISIONS.md` D1–D33; the two sections previously flagged HISTORICAL (Repository Shape, Documentation Set) have been rewritten to the real state. Mission, principles (P1–P6), glossary, architecture, the absence convention, tech stack (a *destination*, not the v0.1 dependency set), and API conventions remain binding. For current status and build-time rationale, read the **Revision 1.2 addenda** in the MASTER_SPEC Preface (the M0–M6 landing notes) and the **D-log through D33**; per-version execution lives in `docs/IMPLEMENTATION_PLAN_v0.1.md`–`_v1.0.md`.
|
|
6
|
+
|
|
7
|
+
## Mission
|
|
8
|
+
|
|
9
|
+
> **Xtalate is the trusted translation layer between computational chemistry file formats — a converter that tells you exactly what it kept, what it lost, and why.**
|
|
10
|
+
|
|
11
|
+
Guiding philosophy: **never silently lose scientific information.** Litmus test for any design decision: *if a user diffed the source and output files by hand, would anything surprise them that Xtalate didn't already tell them about?* If yes, the design is wrong.
|
|
12
|
+
|
|
13
|
+
## Design Principles (P1–P6) — binding, referenced by ID in every doc
|
|
14
|
+
|
|
15
|
+
| ID | Principle |
|
|
16
|
+
|----|-----------|
|
|
17
|
+
| **P1** | Every loss is reported, never assumed. "The user probably doesn't need X" is never a valid reason for silence. |
|
|
18
|
+
| **P2** | No parser talks to another parser. All translation flows through one Canonical Model: `Native File → Canonical Object → Target Format`. No format-to-format shortcuts, ever. |
|
|
19
|
+
| **P3** | Absence is information. The schema distinguishes "never present in the source" (`None`) from "present with value zero/empty." |
|
|
20
|
+
| **P4** | Recover explicitly, never guess. Missing-but-required data is supplied only through an explicit Recovery Workflow, recorded as an Assumption. |
|
|
21
|
+
| **P5** | Know the formats before converting. The Capability Matrix predicts preservation/loss *before* conversion; Validation verifies it *after*. |
|
|
22
|
+
| **P6** | Extensibility over optimization. New formats/features attach at defined seams without core redesign; performance is reclaimed later behind stable interfaces. |
|
|
23
|
+
|
|
24
|
+
One sentence to carry into every doc: **a conversion you can't audit is a conversion you can't trust.**
|
|
25
|
+
|
|
26
|
+
## Binding Glossary (use these terms verbatim; never coin synonyms)
|
|
27
|
+
|
|
28
|
+
| Term | Meaning |
|
|
29
|
+
|---|---|
|
|
30
|
+
| **Canonical Model / Canonical Object** | The single internal schema every parser writes to and every exporter reads from (defined in `02_Canonical_Data_Model.md`). |
|
|
31
|
+
| **Parser** | Reads exactly one native format → produces a Canonical Object. Never writes files, never calls another parser, never defaults an absent field. |
|
|
32
|
+
| **Exporter** | Reads a Canonical Object → writes exactly one native format. Never reads native files. |
|
|
33
|
+
| **Capability Matrix** | Per-format, per-field data structure declaring what each format can/cannot express; drives loss prediction. |
|
|
34
|
+
| **Information Discovery Engine** | Inspects an arbitrary file and reports what's present/absent (✓/✗ per canonical field) without converting it. |
|
|
35
|
+
| **Recovery Engine / Recovery Workflow** | Offers explicit, user-chosen ways to supply data a target format requires but the source lacks; every choice becomes an Assumption. |
|
|
36
|
+
| **Conversion Report** | Structured, machine-readable record of what a conversion kept, dropped, transformed, or assumed. |
|
|
37
|
+
| **Validation Report** | Post-conversion re-parse-and-diff result against the original Canonical Object, within tolerance. |
|
|
38
|
+
| **Discovery Report** | Output of the Information Discovery Engine. |
|
|
39
|
+
| **Provenance** | Canonical Model's record of source software, parser version, and full conversion history (incl. all Assumptions). |
|
|
40
|
+
| **Plugin SDK** | Stable interface for third-party parsers/exporters/analysis modules; first-party formats hold no privileged API. |
|
|
41
|
+
| **Round-trip** | `Format A → Canonical → Format B → Canonical`, diffed within tolerance; primary strategy for catching silent bugs. |
|
|
42
|
+
| **Phase 1 formats** | The seven formats: XYZ, extXYZ, CIF, POSCAR, CONTCAR, XDATCAR, ASE trajectory. (Under the roadmap ladder these do not all land in the "MVP": roadmap v0.2's MVP is four of them — XYZ, extXYZ, POSCAR, CONTCAR, the v0.1 set — and all seven complete at roadmap v0.4. Say "the seven Phase 1 formats," not "the seven MVP formats.") |
|
|
43
|
+
|
|
44
|
+
## Architecture at a Glance
|
|
45
|
+
|
|
46
|
+
Single spine, four advisory subsystems that guide but never bypass it:
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
Native File → Format Sniffer → Parser → Canonical Object → Exporter → Target Format
|
|
50
|
+
↑ ↓
|
|
51
|
+
Information Discovery Capability Matrix
|
|
52
|
+
Recovery Engine (explicit only) → Validation Engine
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
- **Format Sniffer** — identifies format + confidence, selects a parser.
|
|
56
|
+
- **Parser layer** (one per format) — must not read another format or talk to another parser.
|
|
57
|
+
- **Canonical Object** — the only thing that crosses the parser/exporter boundary.
|
|
58
|
+
- **Capability Matrix** — tells the exporter what the target can hold *before* it writes.
|
|
59
|
+
- **Recovery Engine** — fills required-but-missing fields only with explicit user consent, recorded as an Assumption.
|
|
60
|
+
- **Validation Engine** — re-parses the output and diffs it against the source Canonical Object.
|
|
61
|
+
- **API layer** (`backend/`, FastAPI) — thin; validates requests, manages jobs/storage; contains no scientific logic.
|
|
62
|
+
- **Web UI** (`frontend/`, Next.js) — presents the workflow and renders reports faithfully; never re-implements conversion logic or hides losses.
|
|
63
|
+
|
|
64
|
+
## Canonical Model — Absence Convention (P3), normative
|
|
65
|
+
|
|
66
|
+
| State | Representation | Meaning |
|
|
67
|
+
|---|---|---|
|
|
68
|
+
| Absent | `None` | Source file did not contain this information at all. |
|
|
69
|
+
| Present | Actual value (incl. zeros) | Source file contained it; the value is the value. |
|
|
70
|
+
|
|
71
|
+
Parsers are forbidden from defaulting — no zero velocities, no identity lattice, no `energy = 0.0` invented to fill a gap. Schema categories: Geometry, Simulation Cell, Trajectory, Dynamics, Electronic Information, Simulation Metadata, Provenance, User Metadata. Modeled as pydantic v2-style; NumPy arrays in memory, nested JSON lists on the wire; internal coordinates and unit conventions are defined normatively in `02_Canonical_Data_Model.md` — treat field names there as final.
|
|
72
|
+
|
|
73
|
+
## Tech Stack (rationale lives in `01_Architecture.md §4`)
|
|
74
|
+
|
|
75
|
+
> These are destination choices for the versions that need them (Service in v0.5, Web UI in v0.6), not the v0.1 dependency set. v0.1 is a pure-Python library + CLI: pydantic + numpy, plus ASE once the extXYZ parser lands (the sole scientific dependency — pymatgen was rejected as a v0.1 dependency; see `docs/DECISIONS.md` D4, D7).
|
|
76
|
+
|
|
77
|
+
| Layer | Choice |
|
|
78
|
+
|---|---|
|
|
79
|
+
| Frontend | Next.js + React + Tailwind |
|
|
80
|
+
| Backend | FastAPI (Python — must be in-process with ASE/pymatgen) |
|
|
81
|
+
| Scientific core | ASE + pymatgen |
|
|
82
|
+
| Future visualization | Mol* (secondary goal, not MVP) |
|
|
83
|
+
| Relational DB | PostgreSQL (JSONB for report bodies) |
|
|
84
|
+
| Blob storage | S3-compatible object storage, lifecycle-expiring |
|
|
85
|
+
| Job queue | RQ on Redis |
|
|
86
|
+
|
|
87
|
+
## Repository Shape (current v0.1 layout)
|
|
88
|
+
|
|
89
|
+
> The pre-implementation architecture review rejected a solo-maintainer monorepo of separately-packaged components as unnecessary overhead (`docs/ARCHITECTURE_REVIEW.md` §4.1; `docs/DECISIONS.md` D1). The shipped v0.1 layout is **one package** in a `src/` layout — no per-component `pyproject.toml`s. `frontend/`, `backend/`, and `plugins/` do not exist yet; they arrive with the versions that need them (Service at roadmap v0.5, Web UI at v0.6, entry-point plugin discovery at v0.3). `docs/MASTER_SPEC.md` Part 1 §5 is the authoritative tree.
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
src/xtalate/
|
|
93
|
+
schema/ Canonical Model; depends on nothing else in xtalate/ (a.k.a. "canonical-schema")
|
|
94
|
+
sdk/ stable parser/exporter base classes + capability models (a.k.a. "plugin-sdk")
|
|
95
|
+
parsers/ one per format; depends only on schema + sdk
|
|
96
|
+
exporters/ one per format; depends only on schema + sdk
|
|
97
|
+
capabilities/ Capability Matrix: assembles + queries declarations (a.k.a. "capability-matrix")
|
|
98
|
+
discovery/ Information Discovery Engine + Format Sniffer + Discovery Report
|
|
99
|
+
conversion/ Conversion Engine — orchestrates the above + Conversion Report
|
|
100
|
+
recovery/ Recovery Engine + workflows (preset-only in v0.1)
|
|
101
|
+
validation/ Validation Engine + Validation Report
|
|
102
|
+
cli/ thin argparse presenter: inspect / convert / validate / capabilities
|
|
103
|
+
examples/ runnable end-to-end library + CLI samples
|
|
104
|
+
tests/ golden/, roundtrip/, and per-subpackage suites
|
|
105
|
+
docs/ the doc family this file indexes
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Dependency direction is strict and acyclic — enforced physically by an `import-linter` `layers` contract (`pyproject.toml`), run with ruff + mypy --strict + pytest on every PR (`docs/DECISIONS.md` D5). This is what enforces **P2**. The descriptive names in parentheses (`canonical-schema`, `plugin-sdk`, `capability-matrix`) are the prose labels used across the spec; the tree gives the import-name mapping.
|
|
109
|
+
|
|
110
|
+
## API Conventions (full spec in `06_API.md`)
|
|
111
|
+
|
|
112
|
+
- All endpoints under `/v1/`. Path-prefix versioning; additive changes are non-breaking; new formats/scenarios are *values*, not new endpoints.
|
|
113
|
+
- Long-running operations (`inspect`, `convert`, `validate`) are **async jobs**: submit → poll `/v1/jobs/{job_id}` → retrieve result.
|
|
114
|
+
- Job states: `queued → running → completed | failed | cancelled`, plus `queued → failed` (dequeue-precondition failure, Revision 1.1) and `running → awaiting_recovery → running | expired`. An expired `awaiting_recovery` job resolves to a **refused** conversion, never a silently applied default.
|
|
115
|
+
- **A refusal is not an HTTP error.** A conversion the engine declines is a completed job whose `ConversionReport.status == "refused"` — HTTP 200.
|
|
116
|
+
- Single error envelope for all non-2xx responses: `{ error: { code, message, details, request_id, documentation_url } }`. Codes are stable machine strings (e.g. `UNKNOWN_FORMAT`, `PARSE_ERROR`, `VALIDATION_ACK_REQUIRED`, `JOB_ALREADY_TERMINAL`).
|
|
117
|
+
- Response bodies embed the pydantic report models verbatim (`DiscoveryReport`, `ConversionReport`, `ValidationReport`) — no parallel DTOs.
|
|
118
|
+
|
|
119
|
+
*(This describes the v0.5 Service layer's target contract; there is no API yet in v0.1.)*
|
|
120
|
+
|
|
121
|
+
## Documentation Set
|
|
122
|
+
|
|
123
|
+
> `docs/MASTER_SPEC.md` is edited directly as the single source of truth (Revision 1.2, Preface); the standalone `00`–`10` `.md` files named in older prose **never existed as separate committed files** — do not create or assume them. The Part numbering (`04 §3.3` = "Part 4 §3.3") survives only as a stable citation scheme inside the one document. The document family that actually exists:
|
|
124
|
+
|
|
125
|
+
- **`docs/MASTER_SPEC.md`** — the constitution (Parts 0–10 + Appendices; Preface revision log runs Revisions 1.1–1.6). Start here; use its Table of Contents.
|
|
126
|
+
- **`docs/DECISIONS.md`** — build-time decisions log, **D1–D33**, each with a rejected alternative and a standing-rules note at the top.
|
|
127
|
+
- **`docs/ARCHITECTURE_REVIEW.md`** — the accepted pre-implementation review (its acceptance is MASTER_SPEC Revision 1.2).
|
|
128
|
+
- **`docs/Incremental_Roadmap_v1.0.md`** — the solo-developer version ladder (v0.1–v0.7); carries a Revision 1.6 staleness banner over its execution detail, ladder still binding.
|
|
129
|
+
- **`docs/IMPLEMENTATION_PLAN_v0.1.md` … `_v1.0.md`** — per-version execution plans, milestones **M0–M38**, each superseding the roadmap's execution prose for its version.
|
|
130
|
+
- **`docs/DOCS_CONSISTENCY_REVIEW_2026-07.md`** — the post-v0.1 corpus consistency review (findings C1–C11) this refresh implements.
|
|
131
|
+
|
|
132
|
+
> Not committed to this repo (external, historical): `Xtalate_Doc_Prompts.md` — the prompt set that generated the original drafts; superseded by the single-source-of-truth model, do not run it or create the standalone files.
|
|
133
|
+
|
|
134
|
+
## Working Rules for This Project
|
|
135
|
+
|
|
136
|
+
- **Terminology is binding.** Reuse exact field names, endpoint paths, report field names, and component names already established. If a name seems wrong, say so explicitly and explain why — never rename silently.
|
|
137
|
+
- **Write for an isolated reader.** Every doc must stand alone for a contributor or a planning agent who has not read the master prompt directly, while staying consistent with the rest of `docs/`.
|
|
138
|
+
- **Justify nontrivial decisions.** Name at least one reasonable rejected alternative for each nontrivial architectural or design choice.
|
|
139
|
+
- **No silent data loss, ever — including in the docs themselves.** Any design that could cause silent loss must be called out explicitly, not glossed over.
|
|
140
|
+
- **MVP discipline.** Favor maintainability, correctness, and extensibility over convenience; don't overengineer Phase 1, but never foreclose Secondary Goals — name the extensibility seam they'd attach to.
|
|
141
|
+
- **Scope discipline.** Produce only the file(s) a given prompt asks for. Do not pad a doc with content that belongs in a different doc file.
|
|
142
|
+
- **Format.** Professional Markdown; Mermaid for all diagrams (GitHub-renderable); directory trees where relevant; no marketing language — this is engineering documentation, not a pitch.
|
|
143
|
+
- **No AI attribution in commits.** No `Co-Authored-By` AI trailer, no "Generated with …" line, and no AI listed as author or contributor in commit metadata, `CITATION.cff`, or release notes — the human maintainer is the author of record on every commit an agent creates or assists with. (`docs/MASTER_SPEC.md` Preface; `docs/DECISIONS.md` D10.)
|
|
144
|
+
- **Never commit secrets.** No API keys, tokens, database credentials, or other secrets are ever committed to this repository, in code, config, fixtures, or commit messages — not even temporarily, not even in a since-reverted commit (git history is not a safe place to "fix it later"). Secrets are supplied only via environment variables or an untracked local file (`.env`, already gitignored) and referenced by name in code, never inlined. Before staging or committing, check `git status`/`git diff` for anything that looks like a credential — including in filenames that look innocuous — and stop to ask if unsure. If a secret is ever accidentally committed, the fix is rotation (invalidate the leaked credential) *and* history rewrite, not just a follow-up commit that deletes it. There are no secrets in the v0.1 codebase (pure-Python library + CLI, no network calls, no credentials); this rule exists ahead of need because v0.5 (Service layer: database, object storage, hosted-instance API keys) is exactly where a leak would first become possible, and the discipline should already be established by then.
|
|
145
|
+
|