001 plan review pack — for ChatGPT Generated: 2026-08-09T12:23:59+02:00 Constitution: 0.1.5 | Active: specs/001-development-environment ================================================================================ FILE: specs/001-development-environment/plan.md ================================================================================ # Implementation Plan: Development environment on rom-dev01 **Branch**: `001-development-environment` | **Date**: 2026-08-09 | **Spec**: [spec.md](./spec.md) **Input**: Feature specification from `/specs/001-development-environment/spec.md` **Mode**: Documentation/design only until `rom-dev01` exists with working SSH. No implementation, host mutation, or Android sync/build in this planning phase. ## Summary Feature `001` prepares an already-existing Ubuntu 24.04 host (`rom-dev01`) as the ROM development environment: unprivileged `android-build` account, `/srv/rom` layout, Lineage/AOSP host toolchain + ccache, Spec Kit meta-repo bootstrap, private Gitea (four repos) with off-VM Git history mirror, Ansible + Semaphore smoke orchestration, thin-client laptop workflow, and guest isolation posture. It does **not** create the Proxmox VM, sync LineageOS, build Android, or touch the Pixel. Those belong to human Salt/PVE prep (`rom-dev01` prerequisite) and features `002`/`003`+. **Unlocks**: after P1, host is usable for work; after P2, Gitea is canonical and orchestration smoke works → ready for `002` then `003`. ## Technical Context **Language/Version**: Bash / Ansible YAML; Ubuntu Server 24.04 LTS on host; Spec Kit docs in Markdown (English). **Primary Dependencies**: apt packages per current LineageOS/AOSP host build docs at implementation time; `repo`; `git` + `git-lfs`; `ccache`; Gitea; Ansible; Semaphore UI; OpenSSH. **Storage**: Host filesystem paths under `/srv/rom/` (no project-owned LVM/fstab changes). Secrets directory restricted/empty in `001`. **Testing**: Observable shell/service checks (clean-login tool smoke, systemd active, local curl, rsync SHA256, restart survival). No Android instrumentation. **Target Platform**: Single VM `rom-dev01` (~6 vCPU / 52 GB RAM / 2 TB / 32 GB swap) + thin laptop (Cursor Remote SSH, adb/fastboot, rsync). **Project Type**: Infrastructure / host bootstrap for Android ROM development. **Performance Goals**: Toolchain available in a clean `android-build` login; services survive restart; no public exposure of Gitea/Semaphore. Build `-j` tuning is **out of scope** (owned by `003`). **Constraints**: - VM/PVE/IP/Salt `saltenv=base` provisioning is external; do not fight managed config silently. - Never build Android as root; never `chmod 777`. - Never flush firewall / risk active SSH path. - Do not bind Gitea/Semaphore to `0.0.0.0` without an explicit protected design (default: loopback / non-public + SSH tunnel). - No secrets in Git or build logs. - Do not delete laptop staging tree before Gitea + mirror verified. **Scale/Scope**: One developer; four Gitea repos; one smoke Ansible playbook; no distributed CI / registry / Kubernetes. ## Constitution Check *GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.* | Gate | Status | Notes | |------|--------|-------| | Independence from SSYSTEMS governance | PASS | No Redmine/GitLab/company MR workflow; no Salt VM creation from this repo | | Spec-as-source | PASS | Plan derived from `001/spec.md` + constitution `0.1.5` | | Parity/hardening docs non-normative | PASS | Not used as requirements for `001` | | Host boundary: VM is prerequisite | PASS | Preconditions assume host exists; non-goals forbid PVE/qm/Salt states | | Isolation posture (guest) | PASS | Public-NIC workflow; UIs not world-exposed; SSH preserved | | No Android trees on laptop | PASS | Thin-client + `/srv/rom` on host | | Secrets not in Git | PASS | Explicit secrets handling | | Magisk/KSU/microG/signing out of `001` | PASS | Listed in DO NOT | | Capacity honesty | PASS | Plan around 6/52/2T/32; parallelism deferred to `003` | **Post-design re-check**: PASS — artifacts (`research.md`, `quickstart.md`, operational `data-model.md`) stay inside `001` scope; no invented API contracts. ## Project Structure ### Documentation (this feature) ```text specs/001-development-environment/ ├── plan.md # This file ├── research.md # Phase 0 ├── data-model.md # Operational entities only (no app domain model) ├── quickstart.md # Validation / cold-start guide ├── contracts/ # N/A for 001 (see contracts/README.md) └── tasks.md # Created later by /speckit-tasks — NOT this command ``` ### Host layout (target on rom-dev01) ```text /srv/rom/ ├── src/ # future Android trees (empty in 001; no repo sync) ├── references/ # future AXP/reference captures (empty OK) ├── tooling/ # project scripts / ansible / helper tooling ├── ccache/ # CCACHE_DIR for android-build ├── artifacts/ # handoff to laptop (dummy file for smoke) ├── logs/ # install/bootstrap logs └── secrets/ # restricted; empty; no key generation in 001 # service data (exact paths chosen at implement time; document them) # e.g. /var/lib/gitea, /etc/gitea, semaphore data dir # meta Spec Kit tree (staging copy, then Gitea clone) # e.g. /home/android-build/sunfish-a16-rom and/or /srv/rom/tooling/... ``` ### Meta repository content (laptop staging → Gitea) ```text sunfish-a16-rom/ # Spec Kit + docs (this tree) ├── .specify/ ├── specs/ ├── docs/ ├── AGENTS.md └── … ``` Companion empty repos on Gitea (no AOSP trees): `sunfish-rom-manifest`, `sunfish-rom-build`, `sunfish-rom-patches`. **Structure Decision**: Infrastructure feature — host paths + Git/service bootstrap. No application `src/` tree inside Spec Kit for `001`. ## Complexity Tracking > No constitution violations requiring justification. | Violation | Why Needed | Simpler Alternative Rejected Because | |-----------|------------|--------------------------------------| | — | — | — | --- ## 1. Summary (expanded) `001` makes `rom-dev01` a usable ROM workstation and private Git home without performing ROM builds. Completion means: clean `android-build` toolchain smoke passes; `/srv/rom` owned correctly; Spec Kit tree lives on host; Gitea holds four repos and is canonical; laptop has an off-VM bare mirror; Ansible+Semaphore run one smoke playbook; thin-client + isolation docs/checks pass; no PVE/Salt fight, no Pixel/LOS work. ## 2. Constitution check / source of truth Authoritative for this plan: 1. `.specify/memory/constitution.md` **v0.1.5** 2. `specs/001-development-environment/spec.md` 3. `AGENTS.md` Non-normative (do not treat as `001` requirements): `docs/axp-pro-parity.md`, `docs/hardening-backlog.md`. ## 3. Preconditions Assume already true before `/speckit-implement`: - `rom-dev01` exists (human + SSYSTEMS Salt/PVE) - Ubuntu Server 24.04 LTS - One reachable public IPv4 (public NIC only) - SSH administration works - `saltenv=base` already applied - Capacity approximately: 6 vCPU / 52 GB RAM / 2 TB disk / 32 GB swap Do **not** create, resize, or re-IP the VM from this project. ## 4. Non-goals / boundaries **DO NOT** (plan / tasks / implement): - create or configure the Proxmox VM; emit `qm`/PVE provisioning; - choose or hard-code the public IPv4; - write SSYSTEMS Salt states; modify `pm-test01`; - assume company internal DNS/VPN/LDAP/GitLab/NFS/Zabbix is required; - start LineageOS `repo sync` or any Android build; - touch/backup/flash the Pixel (NeoBackup belongs to `002`); - introduce microG, Phonesky, KernelSU, Magisk, SUSFS, or hardening; - generate ROM/AVB/Phonesky signing keys; - put `.repo`, `out/`, or ccache on the laptop; - require Ansible on the laptop; - expose Gitea or Semaphore to the public Internet; - design GitLab/Jenkins/Kubernetes/registry/distributed runners; - turn Semaphore into the Android build pipeline yet; - invent an API/data model merely to satisfy a template; - hard-code facts that must be measured on `rom-dev01`; - duplicate requirements owned by `002`/`003`+. **Implement-time DO NOT** (operational): - run Android builds as root; `chmod 777` project trees; - blindly dist-upgrade; replace apt sources without need; - overwrite Salt/base-managed config silently; - flush firewall/nftables; risk active SSH management path; - bind Gitea/Semaphore to `0.0.0.0` without explicit protected design; - commit passwords/tokens/private keys; plaintext secrets in Ansible in Git; - delete laptop staging before Gitea + mirror verified; - delete/overwrite working data only to fake idempotency; - download full Lineage tree; compile Lineage; connect/flash Pixel; - automate ROM builds through Semaphore yet. **Conflict rule**: If base-managed configuration conflicts with project needs, **stop and report** — do not silently replace or fight it; do not “fix” unrelated infrastructure opportunistically. ## 5. Host preflight (before modifying the host) First implement steps are **observational**. Capture and store under `/srv/rom/logs/preflight/` (or create layout first then log): - Ubuntu version / kernel - CPU / RAM / swap - filesystem free space (esp. for `/` and future `/srv/rom`) - network interfaces / default route (confirm no required internal NIC) - SSH reachability - existing users / groups - existing firewall (nft/ufw/iptables) summary — **do not flush** - listening ports / services - apt sources summary - hints of Salt/base-managed files if obvious Produce a short baseline note in the meta repo docs or `/srv/rom/logs` for later diff. Especially important because `saltenv=base` already touched the machine. ## 6. Account and permission model | Actor | Role | |-------|------| | Existing admin SSH user | packages, systemd, system config, firewall changes (carefully) | | `android-build` | Android sources/builds (later), ccache, artifacts, build tooling; **never** build as root; **no sudo required to compile** | | `gitea` (service user) | Gitea data/config ownership | | `semaphore` (service user/context) | Semaphore data/config ownership | Rules: - No `chmod -R 777 /srv/rom` - Group memberships only as needed (e.g. `android-build` owns `/srv/rom` trees it writes; services own their state dirs) - Admin elevates only for system packaging/services ## 7. `/srv/rom` filesystem layout Create with explicit owners/modes (exact modes chosen at implement; document): ```text /srv/rom/ ├── src/ ├── references/ ├── tooling/ ├── ccache/ ├── artifacts/ ├── logs/ └── secrets/ # empty, restricted; do not generate keys in 001 ``` - Do **not** manage partitions/LVM/fstab from this project. - Do not populate `references/` (that is `002`) or generate secrets. - `artifacts/` must be writable by `android-build` for dummy + later builds. ## 8. Android host toolchain (no sync/build) - Install host packages using **current primary LineageOS/AOSP build documentation at implementation time** (do not freeze a stale 50-package list in this plan). - Record what was installed (log + short note in meta repo / `/srv/rom/logs`). - Ensure `git`, `git-lfs`, `repo`, `python3`, and Java/toolchain prerequisites required by that documentation. - Configure `ccache`: location (`/srv/rom/ccache`), ownership (`android-build`), size cap, environment (`CCACHE_DIR`, etc.). **Not** tied to inventing `-j` from vCPU/RAM here. - FR-007: document capacity (6/52/32) and that **conservative `-j` / OOM tuning belongs to `003`**, with a one-line recommended starting point only if needed for docs — no build execution in `001`. - Smoke from a **clean login**: `sudo -iu android-build` → verify tools; **no `repo sync`**. ## 9. Spec Kit staging bootstrap (before Gitea) Egg/chicken sequence (mandatory): ```text laptop staging: /home/aventura/Workspaces/sunfish-a16-rom ↓ rsync/scp/git bundle (preserve .git) rom-dev01 working copy ↓ install Gitea ↓ create empty Gitea repo sunfish-a16-rom ↓ push meta repo ↓ create sunfish-rom-manifest, sunfish-rom-build, sunfish-rom-patches ↓ Gitea becomes canonical remote ``` Do **not** assume `git clone https://gitea...` before Gitea exists. Do **not** delete laptop staging until Gitea push + off-VM mirror verified. ## 10. Gitea - Install on `rom-dev01` with persistent data dirs and systemd unit. - Default access: **loopback / non-public bind + SSH tunnel** from laptop (decision fixed for `001` unless implement discovers a hard conflict — then stop/report). - Create admin credentials with restricted local storage; never commit secrets. - Verify: service active; local curl succeeds; direct public Internet access to UI **not** required / not open. ## 11. Canonical Git transition Repos: 1. `sunfish-a16-rom` (meta Spec Kit/docs) — push existing history 2. `sunfish-rom-manifest` — empty 3. `sunfish-rom-build` — empty (future Ansible/scripts home) 4. `sunfish-rom-patches` — empty Document remotes for host + laptop clones. Company GitLab must not be used. ## 12. Off-VM Git mirror (P2) First implementation (sufficient for `001` SC-007): ```text Gitea repos → bare mirrors on laptop (or equivalent off-VM path) ``` Borg / full Gitea disaster recovery is **later**, not required to close `001`. Requirement is **Git history** redundancy, not VM image backup. ## 13. Ansible bootstrap (P2) - Install Ansible on `rom-dev01` for **project environment** automation. - Keep playbooks in meta repo or `sunfish-rom-build` once created. - No plaintext secrets in Git (use local ansible vault / restricted files outside Git if needed in `001` — prefer deferred secrets). ## 14. Semaphore bootstrap (P2) - Install Semaphore as UI over Ansible. - Same exposure model as Gitea (loopback/tunnel). - First integration: **small localhost/environment smoke playbook only** — prove Semaphore can run Ansible. **Not** Android build pipeline. ## 15. Isolation / firewall / service exposure Keep guest isolation posture from constitution/`001` spec: - Public NIC only for project workflow - Gitea/Semaphore not world-exposed - Document firewall/DNS expectations Implementation safety: - never flush/replace existing firewall blindly; - never close current SSH path before a tested replacement; - keep a rollback path; - prefer additive allow rules. ## 16. Laptop thin-client workflow Document and smoke: 1. Cursor Remote SSH → host workspace as `android-build` (or documented path) 2. Dummy artifact in `/srv/rom/artifacts` → `rsync` to laptop → SHA256 match 3. adb/fastboot remain on laptop; Pixel USB on laptop; no USB passthrough design ## 17. Verification matrix (FR/SC → evidence) | ID | Implementation focus | Observable verification | |----|----------------------|-------------------------| | FR-001 | Create `android-build` + home/workspace docs | `id android-build`; login shell works | | FR-002 | LOS/AOSP host deps + ccache | clean-shell: `git`, `repo`, `ccache -s`, etc. | | FR-003 | `/srv/rom` tree + ownership | `namei -l` / `stat` shows owners; writable artifacts | | FR-004 | Gitea + four repos | local UI/API; four repos listed | | FR-005 | Ansible + Semaphore smoke | Semaphore runs smoke playbook successfully | | FR-006 | Thin-client docs | doc exists; Remote SSH + rsync smoke done | | FR-007 | Capacity / defer `-j` to `003` | short doc records 6/52/32 and defers build jobs | | FR-008 | Spec Kit on host | tree present; after P2, cloned/pushed via Gitea | | FR-009 | Isolation posture | checklist: NIC, binds, firewall notes; SSH still works | | FR-010 | Off-VM Git mirror | laptop bare mirror of four repos once | | SC-001 | Toolchain smoke | clean `android-build` session has tools for later `003` | | SC-002 | Artifacts handoff | dummy file rsync SHA256 match | | SC-003 | Four Gitea repos | listed on Gitea; not company GitLab | | SC-004 | Cold-start workflow doc | quickstart path SSH→workspace→(later build)→rsync→flash | | SC-005 | No PVE management | no qm/PVE playbooks in repo/tasks evidence | | SC-006 | Isolation checklist | passes without requiring VPN/internal DNS | | SC-007 | Mirror demonstrated | off-VM mirror exists and was verified once | ## 18. Handoff **`001` complete** means environment ready for `002` (phone capture) and later `003` (LOS vanilla build) — **not** “start building Lineage now”. Do not begin `/speckit-plan` for `002`/`003` until `001` doc cycle + implement are done (avoid document saturation). ## P1 / P2 sequencing ### P1 — host usable without Gitea 1. Host preflight baseline 2. Create `android-build` 3. Create `/srv/rom` + permissions 4. Install build prerequisites (current LOS/AOSP docs) 5. `repo` / `git` / `git-lfs` / ccache config 6. Copy laptop staging meta repo to host (preserve `.git`) 7. Clean-shell toolchain smoke 8. Cursor Remote SSH smoke 9. Dummy artifact → rsync → laptop (SHA256) ### P2 — reproducible / comfortable 1. Install Gitea (loopback/tunnel) 2. Create four repos 3. Push meta repo; set Gitea canonical 4. Off-VM bare mirrors on laptop 5. Install Ansible 6. First smoke playbook 7. Install Semaphore 8. Run smoke playbook from Semaphore 9. Isolation / access / firewall validation (SSH-safe) 10. Service restart (+ reboot survival where practical) Finish P2 before formally starting `002`. ## Phase outputs | Artifact | Role | |----------|------| | [research.md](./research.md) | Decisions: Gitea/Semaphore access, mirror, deps source, ccache | | [data-model.md](./data-model.md) | Operational entities only | | [quickstart.md](./quickstart.md) | Cold-start validation | | [contracts/](./contracts/) | N/A | Next command after human review of these files: `/speckit-tasks` (still no VM). Then `/speckit-analyze`. Stop before `/speckit-implement` until SSH to `rom-dev01` works. ================================================================================ FILE: specs/001-development-environment/research.md ================================================================================ # Research: 001-development-environment Phase 0 decisions for planning. Implementation may refine versions using live primary docs on `rom-dev01`. ## R1 — Gitea access model **Decision**: Bind Gitea to loopback (or other non-public address) and access via SSH local port forward from the laptop. Do not require public HTTPS exposure. **Rationale**: Constitution/`001` require private project Git without unnecessary Internet exposure; single developer; SSH already required for admin/Remote SSH. **Alternatives considered**: - Public HTTPS + firewall allowlist — rejected for `001` (more moving parts; accidental exposure risk). - VPN-only bind — rejected as a *required* path (may exist externally; project must not depend on company VPN). ## R2 — Semaphore access model **Decision**: Same as Gitea — loopback/non-public + SSH tunnel. First job is a localhost/environment smoke playbook only. **Rationale**: Semaphore is orchestration UI, not a public portal; Android build pipeline is explicitly out of scope for `001`. **Alternatives considered**: Public Semaphore — rejected. Full ROM pipeline in Semaphore — deferred past `003` manual first build. ## R3 — Off-VM Git history backup **Decision**: Laptop-side **bare mirrors** of the four Gitea repos for `001` (SC-007). Borg/full Gitea DR later. **Rationale**: Spec requires off-VM copy of project Git history, not a complete backup platform. Simplest path that meets SC-007. **Alternatives considered**: Borg-only — optional later. Second server — not needed for `001`. ## R4 — Lineage/AOSP host dependencies **Decision**: At implement time, follow **current primary LineageOS / AOSP host build documentation** for Ubuntu; record installed package set in logs. Do not freeze a long package list in the plan. **Rationale**: Dependency lists drift; stale pins cause false confidence. **Alternatives considered**: Copy a historical package list from blogs — rejected. ## R5 — `repo` installation source **Decision**: Install Google's `repo` launcher via the method recommended by current Lineage/AOSP docs (typically curl to `~/bin/repo` or distro package if still adequate); verify `repo --version` as `android-build`. **Rationale**: Align with upstream bring-up docs used for `003`. ## R6 — ccache policy vs build parallelism **Decision**: `001` configures `CCACHE_DIR=/srv/rom/ccache` (or equivalent), ownership for `android-build`, and a disk size cap. Document host capacity (6 vCPU / 52 GB / 32 GB swap). **Defer `-j` / OOM tuning to `003`.** **Rationale**: ccache is storage/env; parallel link/compile jobs belong to the first real build feature. **Alternatives considered**: Encoding `-j$(nproc)` defaults in `001` — rejected as premature and confusing with FR-007 (document capacity; don't run builds). ## R7 — Meta-repo bootstrap before Gitea **Decision**: Copy laptop staging Git tree to host first (rsync/scp/bundle preserving `.git`), install Gitea, create repos, push, then mark Gitea canonical. Keep laptop staging until mirror verified. **Rationale**: Avoid chicken/egg `git clone` against non-existent Gitea. ## R8 — Coexistence with `saltenv=base` **Decision**: Preflight + stop/report on conflicts with base-managed config. Project Ansible may manage **project** paths/services; must not silently rewrite Salt-owned system policy. **Rationale**: Host is dual-touched (SSYSTEMS base then project). Fighting Salt causes flapping. ## R9 — data-model / contracts **Decision**: No application domain model or HTTP API contracts for `001`. Operational entities only in `data-model.md`. `contracts/` marked N/A. **Rationale**: Infrastructure bootstrap feature; inventing APIs would be template noise. ================================================================================ FILE: specs/001-development-environment/data-model.md ================================================================================ # Data model (operational): 001-development-environment This is **not** an application domain model. It records host/Git/service entities for planning clarity. No ORM, no API schemas. ## Entities ### Host (`rom-dev01`) | Field | Notes | |-------|-------| | OS | Ubuntu 24.04 LTS (precondition) | | Capacity | ~6 vCPU / 52 GB / 2 TB / 32 GB swap | | Network | Public IPv4 NIC; no required internal NIC | | Management | External Salt `saltenv=base` + project config | ### Account `android-build` | Field | Notes | |-------|-------| | Type | Unprivileged login user | | Owns | `/srv/rom/{src,references,tooling,ccache,artifacts,logs}` (as documented) | | Must not | Need root to compile; own Gitea/Semaphore system dirs | ### Path root `/srv/rom` | Child | Purpose | Populated in 001? | |-------|---------|-------------------| | `src/` | Future Android trees | Empty (no sync) | | `references/` | Future AXP/reference | Empty | | `tooling/` | Scripts/ansible helpers | Partial OK | | `ccache/` | CCACHE_DIR | Yes (dir + env) | | `artifacts/` | Laptop handoff | Dummy file for smoke | | `logs/` | Bootstrap/preflight logs | Yes | | `secrets/` | Restricted placeholder | Empty; no keygen | ### GiteaInstance | Field | Notes | |-------|-------| | Bind | Loopback / non-public | | Repos | `sunfish-a16-rom`, `sunfish-rom-manifest`, `sunfish-rom-build`, `sunfish-rom-patches` | | Canonical | Yes after first successful push of meta repo | ### GitMirror (off-VM) | Field | Notes | |-------|-------| | Location | Laptop bare mirrors (initial) | | Scope | All four project repos | | Success | At least one verified mirror cycle | ### AnsibleController / SemaphoreInstance | Field | Notes | |-------|-------| | Host | `rom-dev01` | | First job | Environment smoke playbook | | Exposure | Same private access model as Gitea | ## Relationships ```text AdminSSH ──manages──► Host packages/systemd/firewall android-build ──writes──► /srv/rom (build-related trees) GiteaInstance ──hosts──► four Repositories Laptop ──mirrors──► Repositories (bare) SemaphoreInstance ──runs──► Ansible smoke playbook on Host Laptop Cursor ──RemoteSSH──► Host workspace Laptop ──rsync──► /srv/rom/artifacts (pull) ``` ## State transitions (meta Git) 1. `laptop-staging-only` 2. `copied-to-host` (pre-Gitea) 3. `gitea-canonical` (push complete) 4. `mirrored-off-vm` (SC-007) Do not destroy state (1) until (3) and (4) succeed. ================================================================================ FILE: specs/001-development-environment/quickstart.md ================================================================================ # Quickstart / validation: 001-development-environment Use after `/speckit-implement` on a real `rom-dev01`. Until the VM exists, treat this as the acceptance script shape. ## Prerequisites - SSH to `rom-dev01` as admin works - You are ready to create `android-build` and `/srv/rom` (P1) - Laptop has `rsync`, `ssh`, Cursor Remote SSH ## P1 validation path ### 1. Preflight captured ```bash # On host (admin) — exact commands filled at implement time # Expect: Ubuntu 24.04, disk/RAM/swap notes, firewall summary saved ``` ### 2. Switch to build user ```bash sudo -iu android-build pwd # documented workspace ``` ### 3. Toolchain smoke (clean shell) ```bash git --version git lfs version repo --version python3 --version ccache -s # plus java/other tools required by current LOS/AOSP host docs ``` Expected: all commands succeed without sudo. ### 4. Layout ```bash namei -l /srv/rom /srv/rom/artifacts /srv/rom/ccache touch /srv/rom/artifacts/.write_test && rm /srv/rom/artifacts/.write_test ``` ### 5. Meta repo present on host (pre-Gitea OK) ```bash # path documented at implement — must contain .git and specs/ test -d /path/to/sunfish-a16-rom/.git test -f /path/to/sunfish-a16-rom/specs/001-development-environment/spec.md ``` ### 6. Cursor Remote SSH From laptop: open host workspace; edit a trivial file; confirm it is on the VM disk (not only local). ### 7. Artifact rsync smoke On host: ```bash echo '001-smoke' > /srv/rom/artifacts/dummy-001.txt sha256sum /srv/rom/artifacts/dummy-001.txt ``` On laptop: ```bash rsync -avz USER@ROM_DEV01:/srv/rom/artifacts/dummy-001.txt ./ sha256sum ./dummy-001.txt # must match ``` ## P2 validation path ### 8. Gitea private access ```bash # SSH tunnel example (ports chosen at implement): # ssh -L 3000:127.0.0.1:3000 USER@ROM_DEV01 # curl -fsS http://127.0.0.1:3000/ → success via tunnel ``` Confirm UI not relied upon as public Internet service. ### 9. Four repositories In Gitea: `sunfish-a16-rom`, `sunfish-rom-manifest`, `sunfish-rom-build`, `sunfish-rom-patches`. Meta repo history visible after push. ### 10. Off-VM mirror On laptop: bare clones/mirrors of all four; `git fsck` or fetch succeeds. ### 11. Semaphore smoke Semaphore runs the environment smoke playbook successfully (localhost/host check only — not Android build). ### 12. Restart survival ```bash sudo systemctl restart gitea # unit names as installed sudo systemctl restart # optional: reboot when safe; confirm SSH + services return ``` ### 13. Isolation checklist - No required internal NIC for workflow - Gitea/Semaphore binds documented as non-public - SSH still works after firewall changes - Day-to-day docs do not require company VPN/internal DNS ## Done means `001` complete → start **`002`** (AXP capture / NeoBackup). Do **not** start Lineage `repo sync` / build as part of `001` success. ================================================================================ FILE: specs/001-development-environment/contracts/README.md ================================================================================ # Contracts: N/A for 001 Feature `001-development-environment` is host/Git/service bootstrap. It does **not** expose a project-owned HTTP API, CLI contract for external consumers, or IPC schema that needs `contracts/` files. Service access is operational (SSH tunnel to Gitea/Semaphore UIs) and is documented in `plan.md` / `quickstart.md` instead.