Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 30 additions & 20 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ overrides any default your tooling has.

## 2. Nothing is created outside this repository

Everything your work produces — worktrees, agent prompts, logs, measurements, experiments, scratch
files — lives **inside this repository's root directory.**
Everything your work produces (worktrees, agent prompts, logs, measurements, experiments, scratch
files) lives **inside this repository's root directory.**

| What | Where |
|---|---|
Expand All @@ -27,8 +27,8 @@ files — lives **inside this repository's root directory.**

Before writing a file, check that its absolute path starts with this repository's root. If it does
not, stop. The only exceptions are a path the user names explicitly, and caches that build tools
manage themselves. **Re-pointing a shared cache or a home-directory symlink reaches other projects —
ask first.**
manage themselves. **Re-pointing a shared cache or a home-directory symlink reaches other projects.
Ask first.**

Writing to *another* repository is not an exception either. Do it only when told to work there.

Expand All @@ -41,8 +41,8 @@ existing modules and directories: source inside the package (`pythonx/`), CI scr
The standing root entries are `pyproject.toml`, `README.md`, `LICENSE`, `PROJECT.md`, `AGENTS.md`,
`pythonx/`, `scripts/` (approved by the maintainer, 2026-10-03), `tests/`, `docs/`, `.github/`,
`.gitignore` and `.gitattributes`, plus the maintainer's
untracked `UI.ipynb`. If a new top-level entry seems necessary, propose it — what it is, why, and
why it cannot live inside an existing directory — and wait for approval.
untracked `UI.ipynb`. If a new top-level entry seems necessary, propose it (what it is, why, and
why it cannot live inside an existing directory) and wait for approval.

## 3. Worktrees link large artefacts instead of copying them

Expand Down Expand Up @@ -80,7 +80,7 @@ Every new feature goes through an issue and a pull request:

1. Before starting, search the repository's issues (`gh issue list --state all --search "<keywords>"`).
2. If no issue covers the work, open one (`gh issue create`) stating what and why, and the
completion criterion — which tests must pass.
completion criterion, that is, which tests must pass.
3. Work on a `feat/<topic>` branch, push every commit, and open a pull request into `develop`
whose body contains `Closes #<number>`.
4. Merge into `develop` through that pull request (`gh pr merge --merge --delete-branch`), not by a
Expand All @@ -104,18 +104,18 @@ If a request conflicts with `docs/INTENT.md`, say so instead of implementing it.

## 6. User-authored files are specification

Files the user wrote by hand — notebooks, example build files, sample apps — are the specification.
Files the user wrote by hand (notebooks, example build files, sample apps) are the specification.
Read them **first**. Never delete, rewrite, or `git add` them without being told to. Generated
documentation (roadmaps, design notes) is a record of work, not a requirement; when the two
disagree, the user's file wins.

## 7. Show a conclusion before acting on it

Anything beyond the immediate request — another repository, a public API signature, deleting
files, killing processes, force-pushing, changing branch protection — state what you would do and
Anything beyond the immediate request (another repository, a public API signature, deleting
files, killing processes, force-pushing, changing branch protection): state what you would do and
why, and wait. Investigating, measuring, and reporting are always fine.

**Push every commit right away.** After you commit — on a work branch or on `develop` — push it to
**Push every commit right away.** After you commit, on a work branch or on `develop`, push it to
the remote immediately; no confirmation is needed. Never push to `main` or `release` by hand, and
never force-push without the user's explicit approval.

Expand Down Expand Up @@ -155,17 +155,17 @@ to implement", say what you counted against.
- Give every agent prompt the absolute paths it may write to, and repeat rule 2 in it.
- **Subagents do not run heavy local builds.** Subagents write code, design, investigate, review
and document. Gradle builds, cargo builds, the test gate and model runs are done by the session
itself — one at a time on this machine — or by CI (GitHub Actions) on a pushed branch. Several
itself (one at a time on this machine) or by CI (GitHub Actions) on a pushed branch. Several
sessions share one machine; parallel local builds slow every one of them.

---

# Repository-specific rules — `pythonx-compose`
# Repository-specific rules: `pythonx-compose`

## 11. The specification is the user's notebook

`UI.ipynb` is this project's user-facing specification. It lives only in the maintainer's main
checkout, is git-ignored, and is **never** copied, moved, edited, or `git add`ed — not even into a
checkout, is git-ignored, and is **never** copied, moved, edited, or `git add`ed, not even into a
worktree so a test can see it. Read it in place.

- Its function signatures are **naming examples**. The rule they illustrate is: expose the original
Expand All @@ -181,7 +181,7 @@ worktree so a test can see it. Read it in place.

- `python-multiplatform` exposes Kotlin declarations under their **Kotlin** names
(`androidx.compose.material3`). It must never map `androidx` to `pythonx` or rename any Kotlin
namespace. Do not propose, add, or depend on such a feature there — including in test harnesses.
namespace. Do not propose, add, or depend on such a feature there, including in test harnesses.
- `pythonx/` is a **real package on disk**. Its modules import the `androidx.compose.*` modules the
binder exposes and restructure them Pythonically. A design in which something else synthesises
`pythonx.*` (for example with `__path__ = []`) and the on-disk files cannot be imported is ruled
Expand All @@ -200,7 +200,7 @@ worktree so a test can see it. Read it in place.

## 14. Running the tests

python3 -m pytest tests -q
uv run --with pytest --with mypy pytest tests -q

- The suite needs `pytest`; if the system Python has none, create a virtual environment under
`.tmp/` (rule 2), not in the home directory.
Expand Down Expand Up @@ -229,22 +229,32 @@ worktree so a test can see it. Read it in place.
publishes through PyPI trusted publishing (environment `pypi`). Pushing a tag alone publishes
nothing. Never upload by hand, and never store a PyPI token.
- `.pyi` stubs, `py.typed` and the manifest must end up **inside the wheel**. A `package-data`
pattern is not proof — build the wheel and list it before claiming something ships.
pattern is not proof: build the wheel and list it before claiming something ships.

## 16. Things you do not change without the maintainer

- **The root layout** — see section 2, "Do not add top-level folders".
- **The root layout**: see section 2, "Do not add top-level folders".
- The retired `test/` sample, `pythonx/compose/lite/` and the `pythonx/compose/native` submodule
were removed in #60; they stay reachable through the tag `archive/pre-restructure`. Do not bring
them back.

## 17. Documentation layout

- `README.md` (English) and `docs/locale/README_ko.md` (Korean) say the same thing; change both.
- Write in the house style, `docs/style/writing.md`: voice, the English and Korean pairing, terms
and mechanics. It is the reference for every thisisthepy repository's documentation.
- `docs/guide/` is the bilingual GitHub Pages site. Every visible string exists in English and
Korean. `python3 .github/scripts/check_guide.py` is its checker, and `tests/test_guide.py` runs it
Korean. `uv run python .github/scripts/check_guide.py` is its checker, and `tests/test_guide.py` runs it
in the suite.
- No Markdown files directly under `docs/` other than `INTENT.md` and `SPEC.md`; other documents go
in a topic subdirectory. `README.md` must not link to `AGENTS.md`, `PROJECT.md`,
`docs/INTENT.md` or `docs/SPEC.md` — those files do not exist on `main`.
`docs/INTENT.md` or `docs/SPEC.md`, because those files do not exist on `main`.
- There is no `CLAUDE.md`: Claude Code reads this file directly. Rules go here.
- No em-dash (U+2014) anywhere in documents or code: Markdown, the guide's HTML and CSS, workflows,
`pyproject.toml`, Python comments, docstrings and strings, tests. Split the sentence or use a
comma, colon or parentheses, whichever reads best. The en-dash in ranges (2023–2024, 3.11–3.13)
is fine.
- Install and run examples use only uv, ppp (pypackpack) and tcl (toolchain-lite): `uv add
--prerelease allow pythonx-compose`, `ppp core add "pythonx-compose==0.1.0a1"`, `tcl install
pythonx-compose`, `uv run ...`. No `pip` examples. The published 0.1.0a1 Release notes are a
record and stay as they are.
26 changes: 13 additions & 13 deletions PROJECT.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# PROJECT — pythonx-compose
# PROJECT: pythonx-compose

프로젝트 운영에 필요한 핵심 사항을 정리한 문서입니다. 의도는 `docs/INTENT.md`, 동작 계약은
`docs/SPEC.md`, 에이전트 규정은 `AGENTS.md` 에 있습니다.
Expand Down Expand Up @@ -28,13 +28,13 @@ Compose(`androidx.compose.*`)를 파이썬에서 쓰기 위한 pip 패키지 **`
| 상태 | 항목 |
|---|---|
| 구현 | 매핑 매니페스트 `pythonx-map.toml`, `@Composable` 항등 데코레이터, 실제 디스크 패키지 `pythonx` 와 모듈 수준 규칙 기반 재노출(snake_case 이름·키워드·시그니처, 오버로드 디스패치, 값 클래스 허용 목록), 실제 Compose 1.11.1 에서 생성한 `.pyi` 스텁과 `py.typed`(wheel 포함, mypy 로 검사), `Alignment`/`Arrangement` 상수(평면 `Alignment.End` 와 묶음 `Alignment.Horizontal.End` 모두, #9), proxy 메서드의 snake_case 키워드 인자(`m.padding(padding_values=...)`, python-multiplatform `describe_member` 필요; Kotlin 이름도 실행 시 그대로 통하고 스텁은 snake_case 만) |
| 부분 | 선언형 앱 루트 `@app`·`state`·`app_root`(#11; 로직은 테스트됨, 바인더 경로는 #38 모양의 가짜 호스트로 테스트됨, 실제 Compose 증거는 E2E 모듈 #19; 숫자·문자열 상태는 python-multiplatform #69 부터 왕복됨), 배포 설정, Material 3 위젯(렌더 증거는 python-multiplatform 에만), `Icon`(import 만 됨)·`DefaultIcons`(`Icons.Default` alias, 가짜 호스트로만 테스트됨, 스텁에서는 `DefaultIcons.Add` 가 `ImageVector`; `DefaultIcons.Add` 로 쓴다 — 노트북의 `DefaultIcons.Add()` 가 아님), `TextField(state=...)`·`TextFieldState`(#10; `pythonx.compose.foundation.text.input`, 가짜 호스트로 테스트됨, 노트북의 `text_state=`/`padding=` 는 `state=`/`modifier=Modifier.padding(8)`; IME 조합 증거는 python-multiplatform E2E #26) |
| 부분 | 선언형 앱 루트 `@app`·`state`·`app_root`(#11; 로직은 테스트됨, 바인더 경로는 #38 모양의 가짜 호스트로 테스트됨, 실제 Compose 증거는 E2E 모듈 #19; 숫자·문자열 상태는 python-multiplatform #69 부터 왕복됨), 배포 설정, Material 3 위젯(렌더 증거는 python-multiplatform 에만), `Icon`(import 만 됨)·`DefaultIcons`(`Icons.Default` alias, 가짜 호스트로만 테스트됨, 스텁에서는 `DefaultIcons.Add` 가 `ImageVector`; `DefaultIcons.Add` 로 쓴다; 노트북의 `DefaultIcons.Add()` 가 아님), `TextField(state=...)`·`TextFieldState`(#10; `pythonx.compose.foundation.text.input`, 가짜 호스트로 테스트됨, 노트북의 `text_state=`/`padding=` 는 `state=`/`modifier=Modifier.padding(8)`; IME 조합 증거는 python-multiplatform E2E #26) |
| 계획 | `remember_saveable`·색 스킴·코루틴 스코프 |

### 테스트 기준선

```
python3 -m pytest tests -q
uv run --with pytest --with mypy pytest tests -q
PythonMultiplatform 체크아웃 없음 140 passed, 112 skipped
python-multiplatform develop 31c092f0+ 251 passed, 1 skipped
describe_member 없는 체크아웃 같은 수, 그중 7 개 건너뜀 (python-multiplatform #54 이전)
Expand Down Expand Up @@ -84,9 +84,9 @@ docs/INTENT.md docs/SPEC.md docs/locale/ docs/guide/
## 5. 빌드·테스트

```bash
python3 -m pytest tests -q # 파이썬 테스트
PYTHONMULTIPLATFORM_HOME=<checkout> python3 -m pytest tests -q # 바인더 계층 포함
python3 .github/scripts/check_guide.py # 가이드: HTML 파싱, 링크, en/ko 짝 (tests/test_guide.py 도 실행)
uv run --with pytest --with mypy pytest tests -q # 파이썬 테스트
PYTHONMULTIPLATFORM_HOME=<checkout> uv run --with pytest --with mypy pytest tests -q # 바인더 계층 포함
uv run python .github/scripts/check_guide.py # 가이드: HTML 파싱, 링크, en/ko 짝 (tests/test_guide.py 도 실행)
```

- pytest 가 없으면 `.tmp/` 아래에 가상환경을 만든다 (홈 디렉터리 금지).
Expand All @@ -106,10 +106,10 @@ python3 .github/scripts/check_guide.py # 가이드: HTML 파싱, 링

## 7. 열린 질문 (메인테이너 결정 필요)

1. **코루틴 스코프** — 노트북이 import 하는 `DefaultCoroutineScope`·`MainCoroutineScope`.
2. **`pythonx-map.toml` 의 이중 표기** — `pythonx.compose.layout` 과
1. **코루틴 스코프**: 노트북이 import 하는 `DefaultCoroutineScope`·`MainCoroutineScope`.
2. **`pythonx-map.toml` 의 이중 표기**: `pythonx.compose.layout` 과
`pythonx.compose.foundation.layout` 를 둘 다 유지할지.
3. **릴리스·Pages 활성화** — `.github/scripts/release/` 와 `.github/workflows/`(release-sync, pages)는
3. **릴리스·Pages 활성화**: `.github/scripts/release/` 와 `.github/workflows/`(release-sync, pages)는
들어와 있다. main 보호는 메인테이너가 저장소 설정에서 직접 관리하며 main 은 잠겨 있다. 스크립트나
에이전트는 보호 설정을 만들거나 바꾸지 않는다. release→main PR 머지는 메인테이너가 한다.

Expand All @@ -120,7 +120,7 @@ python3 .github/scripts/check_guide.py # 가이드: HTML 파싱, 링
태그 `archive/pre-restructure` 로 되찾을 수 있다. `tools/` 는 없앴다: 스텁 생성기는
`scripts/`(메인테이너 승인), 가이드 검사기와 릴리스 스크립트는 `.github/scripts/`.

### 결정됨 (2026-10-03, #62) — 첫 PyPI 릴리스
### 결정됨 (2026-10-03, #62): 첫 PyPI 릴리스

- 워크플로 이름: `tests.yml` → `test.yml`, PyPI 배포는 `publish-pypi.yml`.
- 첫 버전은 `0.1.0a1`(알파, 프리릴리스). PyPI 메타데이터(설명·README·라이선스·저자·URL·분류자)는 채웠고,
Expand All @@ -129,8 +129,8 @@ python3 .github/scripts/check_guide.py # 가이드: HTML 파싱, 링
`publish-pypi.yml` 이 Release 태그와 `pyproject.toml` 버전을 대조하고, sdist·wheel 을 빌드하고,
`twine check`·새 venv 스모크를 거쳐 트러스티드 퍼블리싱으로 올린다. 태그만 푸시해서는 아무것도
올라가지 않는다(같은 버전은 PyPI 에 다시 올릴 수 없으므로 의도적인 단계로 둠). **현황: 0.1.0a1 배포됨**(2026-10-03, develop `134a641` 에서 pre-release
`v0.1.0a1`, publish-pypi run 37120641414; https://pypi.org/project/pythonx-compose/). 새 venv 에서
`pip install pythonx-compose==0.1.0a1` 후 import 스모크 통과.
`v0.1.0a1`, publish-pypi run 37120641414; https://pypi.org/project/pythonx-compose/). PyPI 에서 새로
설치한 0.1.0a1 이 import 스모크를 통과했다.

### 결정됨 (2026-10-03, `docs/INTENT.md` §5)

Expand All @@ -142,7 +142,7 @@ python3 .github/scripts/check_guide.py # 가이드: HTML 파싱, 링
- **정렬 상수** 는 `Alignment.End` 와 `Alignment.Horizontal.End` 둘 다 지원한다.
- 소문자 `modifier` 는 두지 않는다(`Modifier` 로 쓴다). ARGB 정수 색은 받지 않는다
(`Color(0xFFFF0000)`). `Spacer(start=...)` 는 지원하지 않는다(`Modifier.padding`). `DefaultIcons` 는
`Icons.Default` 의 alias(매니페스트 `[aliases]` 의 `{ DefaultIcons = "Icons.Default" }`, 읽을 때마다 해석). — `docs/INTENT.md` §5.4–5.7
`Icons.Default` 의 alias(매니페스트 `[aliases]` 의 `{ DefaultIcons = "Icons.Default" }`, 읽을 때마다 해석). 근거는 `docs/INTENT.md` §5.4–5.7.
- 합성된 `pythonx` 를 전제한 테스트·독스트링은 #7 에서 정리했다.

## 8. 관련 저장소
Expand Down
Loading
Loading