Files
AXP-Resurection/docs/001-plan-review-pack.txt
sunfish-admin 12d2179c20 AXP Magisk resurrection track: Speckit 009 port AXP/LOS22 to LOS23.
Ship Magisk like GOLDEN; KernelSU optional recycle only; parallel to sunfish-a16-rom standby.
2026-09-16 21:56:22 +00:00

782 lines
28 KiB
Plaintext

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 <semaphore>
# 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.