Resolves F17 in docker-build-optimization-roadmap.
Root cause:
Gitea's app.ini ROOT_URL was http://localhost:3300/. Gitea bakes
ROOT_URL into the dist.tarball field of every published package's
metadata. Inside a Docker container, 'localhost' is the container
itself, not the host \u2014 so any 'pnpm install' that needed to fetch
a tarball would ECONNREFUSED, even though the registry metadata
itself was reachable via host.docker.internal.
Server-side fix (not in git, requires manual replication on each dev
machine; documented in roadmap \u00a73 A-pre-6):
- Edit /opt/homebrew/var/gitea/custom/conf/app.ini:
ROOT_URL = http://host.docker.internal:3300/
- brew services restart gitea
- sudo sh -c 'echo "127.0.0.1 host.docker.internal" >> /etc/hosts'
Repo-side fix (this commit):
- switch-network.sh: add host.docker.internal to NO_PROXY +
NPM_CONFIG_NOPROXY when NETWORK=corp. Required so host-side curl/
pnpm/npm bypass the corporate proxy (cso.proxy.att.com) when
resolving host.docker.internal. Without this, host installs fail
with the corp proxy's 'Unknown Host' 504 page.
Republished all 64 @bytelyst/* packages so tarball URLs reflect the
new ROOT_URL:
- .publish-manifest.json: 64 entries with new content hashes
- packages/*/package.json: 64 patch-version bumps
(auto-bumped by publish-outdated-packages.sh because previous
versions already existed in registry)
Verification:
curl http://localhost:3300/.../@bytelyst%2Ferrors | jq .dist.tarball
→ http://host.docker.internal:3300/.../errors-0.1.11.tgz (was localhost:3300)
workspace:* refs across all 64 packages: 0
Unblocks: A0-V on every pilot. Verified PASSING on learning_ai_clock:
backend cold build: 59.2 s
web cold build: 3:13 (193 s)
Both via Gitea registry, no docker-prep.sh tarballs needed.
Resolves F16 in docker-build-optimization-roadmap v5.
Root cause:
publish-outdated-packages.sh uses a pack-extract-repack pattern:
1. pnpm pack (rewrites workspace:* in tarball)
2. extract
3. npm pack (re-tar from extracted content)
4. npm publish
Step 3 is the bug. npm pack does not recognize the pnpm-specific
workspace: protocol — it treats workspace:* as a literal version
string and passes it through to the final tarball. Result: any
consumer doing 'pnpm install' inside Docker (where there is no
workspace context) fails with ERR_PNPM_WORKSPACE_PKG_NOT_FOUND.
Documented in roadmap §0 F16 + §3 Phase A-pre.
Fix (publish-outdated-packages.sh):
- Insert a workspace:* rewriter between publishConfig strip and
npm pack. Reads source package.json for each @bytelyst/* target,
resolves workspace:* / workspace:^ / workspace:~ to ^x.y.z.
- Add defense-in-depth: grep the post-rewrite package.json for any
surviving 'workspace:' literal. If found, refuse to publish.
Republished 10 affected packages with workspace:* → resolved semver:
@bytelyst/auth 0.1.5 → 0.1.6
@bytelyst/diagnostics-client 0.1.6 → 0.1.7
@bytelyst/events 0.1.5 → 0.1.6
@bytelyst/extraction 0.1.5 → 0.1.6
@bytelyst/fastify-auth 0.1.5 → 0.1.6
@bytelyst/fastify-core 0.1.5 → 0.1.6
@bytelyst/feedback-client 0.1.6 → 0.1.7
@bytelyst/field-encrypt 0.1.6 → 0.1.7
@bytelyst/react-auth 0.1.6 → 0.1.7
@bytelyst/sync 0.1.5 → 0.1.6
Verification: all 10 packages now scan with 0 workspace:* refs in
their published package.json (per registry curl scan).
Unblocks: A0-V verification on learning_ai_clock (currently blocked
at learning_ai_clock@0be887288).
Idempotent end-to-end Gitea bootstrap for Azure VM (or any Linux host
with Docker available). Replaces manual SSH-and-paste workflow.
Steps (each skippable on re-run):
1. Install Docker via official script (skip with --skip-docker)
2. Write /etc/gitea/docker-compose.yml with package registry enabled
3. Start gitea container, wait for HTTP :3300
4. Create admin user via 'gitea admin user create' (CLI inside container,
no auth bootstrap needed)
5. Create npm-user (learning_ai_user) via admin API
6. Mint npm-scoped token with write:package + read:package
Two execution modes:
- On the VM directly: scp + ssh + run
- Locally targeting remote: --ssh-host azureuser@vm
Outputs npm token to --output FILE or stdout. Prints copy-paste-ready
command for writing to ~/.gitea_npm_token_home on the workstation.
Final summary prints the doctor.sh verification command so user can
confirm registry reachability from their laptop in one step.
--dry-run shows planned actions without execution.
--force re-creates users (use after manual deletion).
Closes the 'cloud VM bootstrap' gap identified during the Gitea hardening
review — pairs with scripts/gitea/{doctor,token}.sh from commit 610a59fd.
Eliminates the three operational pain points hit in the last
owner-rename incident:
1. Owner-rename drift across 14 repos
- npmrc.template now uses ${GITEA_NPM_OWNER:-learning_ai_user}
- switch-network.sh exports GITEA_NPM_OWNER on shell start
- Future renames are a one-line env change, not 14 git commits
2. Stale shell-env tokens (file rotated, env didn't)
- scripts/gitea/token.sh: status|print|validate|rotate subcommands
- 'eval "$(bash scripts/gitea/token.sh print --export)"' refreshes
any shell without re-sourcing ~/.zshrc
- rotate uses Gitea API + macOS Keychain for admin creds
3. No pre-deploy validation
- scripts/gitea/doctor.sh: NETWORK + DNS + token consistency +
registry HTTP 200 + optional package@version probe
- Run before any deploy that needs @bytelyst/* from Gitea
1) Dual-numbering reconciliation
- ROADMAP groups Phase 1 by topic (1.1-1.8); PRH groups by execution
day (1.A-1.F). Added bidirectional mapping table to both docs so
agents can cross-reference any phase reference unambiguously.
2) Fresh-agent quick pointer at top of ROADMAP
- New section tells a new agent exactly which 4 docs to read, in
what order, and which task to pick up first (1.A from the tracker).
3) Broken sub-roadmap links neutralised
- 03_RICH_ITEMS_ROADMAP.md, 04_AGENT_API_ROADMAP.md,
05_INTAKE_ROADMAP.md were linked but did not exist. Replaced with
plain text + 'create when Phase N begins' note so the link doesn't
404. Matches the pattern already used in IMPLEMENTATION_TRACKER.
4) Runbook stubs created (Phase 1.F.11/1.F.12 placeholders)
- docs/runbooks/MEK_ROTATION.md — adapted from NoteLett bcad7d3
- docs/runbooks/SECRET_MANAGEMENT.md — secret inventory + resolution
path + compromise procedure + PII scrubbing rule
Each is a stub now; full content lands when Phase 1.F executes.
5) Stale 'today' wording removed from PRH baseline table
- Replaced 'after fix today' with 'as of 2026-05-25' so the doc
ages cleanly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Moves 5 related docs into docs/devops/gitea-runner/ to keep this
multi-doc workstream from colliding with future roadmaps and
delegation prompts in docs/devops/.
Renames:
HOSTINGER_GITEA_RUNNER_ROADMAP.md -> ROADMAP.md
HOSTINGER_GITEA_ACT_RUNNER_SETUP.md -> ACT_RUNNER_SETUP.md
GITEA_PACKAGES_PUBLISH_WORKFLOW.md -> PUBLISH_WORKFLOW.md
HOSTINGER_GITHUB_RUNNER_SETUP.md -> _PLAN_B_GITHUB_RUNNER.md
CODEX_DELEGATION_PROMPT.md -> (same name, moved)
All internal cross-links updated via sed sweep. Verified no stale
references remain.
Adds README.md in the new folder as the index + pattern doc for
future multi-doc workstreams (one-liner handoff, file map,
architecture summary).
Updated one-liner handoff path:
Read docs/devops/gitea-runner/CODEX_DELEGATION_PROMPT.md ...
Captures the exact bootstrap prompt to paste to Codex on the
Hostinger VM, plus a one-liner that just points Codex at the prompt
file (after Codex has the repo).
Also documents how to monitor Codex's progress from Cascade side
(grep roadmap-update commits) and how to recover if it gets stuck
(the checkbox state in the roadmap IS the resume pointer).
Adds HOSTINGER_GITEA_RUNNER_ROADMAP.md — a single execution tracker
that Codex on the Hostinger VM works through phase-by-phase, ticking
checkboxes and recording commit hashes as it goes.
Structure:
- 6 phases (P0 Pre-flight → P5 First real release) + P6 review handoff
- Each task: [ ] checkbox + Commit hash field + Status note
- Detail steps live in the two companion docs (act_runner setup +
publish workflow); the roadmap is the orchestrator
- Final report section Codex fills in when P0-P5 are complete
- Human review checklist (R1-R9) for verification after handoff
- Operating notes: commit message format, when to ask, never-do list
- Change log table Codex auto-appends to
Critical invariant repeated at P3.6 and P5.4: cross-Gitea SHA1
comparison must match. If it doesn't, Codex stops — it's the
load-bearing architectural guarantee that the dual-Gitea, no-sync-
script model rests on.
Also adds roadmap-pointer banners to the two companion docs
(HOSTINGER_GITEA_ACT_RUNNER_SETUP.md, GITEA_PACKAGES_PUBLISH_WORKFLOW.md)
so anyone landing there knows the master tracker exists.
Adds two new docs and a banner on the existing GitHub-runner doc.
WHY: the user already has Gitea Actions configured across all 20+
repos (.gitea/workflows/ci.yml). Building a parallel GitHub Actions
self-hosted runner pipeline is unnecessary work that also drags in
GitHub Organization migration pressure (with Vercel/Netlify pricing
side-effects on free tiers).
The canonical architecture instead:
- Each Gitea instance (corp Mac local + Hostinger VM) runs its own
act_runner.
- A single publish-packages.yml workflow lives in every package-
publishing repo.
- When the same git tag is pushed to both Giteas, each one builds
inside the same pinned Docker image (node:20-bookworm@sha256:...)
with the same lockfile, producing BYTE-IDENTICAL tarballs.
- No sync script is needed; the shared git tag IS the sync mechanism.
- Lockfile integrity hashes match across both registries, so corp Mac
and personal Mac + Hostinger prod all see the same packages.
New: HOSTINGER_GITEA_ACT_RUNNER_SETUP.md
- Codex-actionable prompt to install act_runner on the Hostinger VM
- Pre-flight checks (arch detection, Docker daemon, Gitea reachable)
- Idempotent user creation, SHA-verified binary download
- Docker mode runner config with labels mapping ubuntu-latest to
pinned Node image
- Smoke test + full E2E with throwaway @bytelyst/_runner-e2e-test
package
- The architectural invariant check: cross-Gitea SHA comparison —
same tag pushed to both must produce identical tarballs
- Monitoring (Gitea UI, API, systemd journal)
- Hardening, rollback, deliverables, guardrails, questions
New: GITEA_PACKAGES_PUBLISH_WORKFLOW.md
- The actual publish-packages.yml triggered by v* tags
- Docker image pinned by digest for build determinism
- pnpm@9.12.0 pinned, --frozen-lockfile, host-network container
- Token mounted as read-only secret file (not env var)
- Concurrency cancel-in-progress: false (never cancel a publish)
- Pack tarballs + SHA512 manifest as Gitea Release assets for audit
trail
- Two propagation strategies: reusable workflow (preferred) vs
sync-publish-workflow.sh script
- Operator runbook for cutting a release
- Failure-mode table + remediation
- Deliverables checklist
Updated: HOSTINGER_GITHUB_RUNNER_SETUP.md
- Added 'PLAN B' banner at the top
- Cross-links to the Gitea Actions docs
- Kept the doc intact as a valid alternative if priorities ever
shift to making GitHub Actions the publish driver
Adds the missing pieces revealed during review:
§1 Multi-repo registration decision — choose repo-level vs org-level
up-front. Default doc remains repo-level, but explicitly calls out
org-level as the scaling path for 20+ repos.
§2 Pre-flight check additions:
- Arch detection (x86_64 / aarch64) before downloading runner tarball
- github.com + objects.githubusercontent.com reachability check
- gh CLI auth status check (must be saravanakumardb1)
§4 Installation hardening:
- Step 1 is now idempotent (getent guards on useradd/usermod)
- Step 3 queries latest runner version via gh api (no more stale pin)
- Step 3 includes SHA256 verification of the downloaded tarball
against the release-notes manifest, with explicit STOP-if-mismatch
- Step 3 has REGISTRATION_URL var with commented Option A/B for
repo-level vs org-level scope
§5 Smoke test — added explicit git checkout/add/commit/push commands
for creating the runner/smoke branch (was implicit before).
§8 (renamed) — comprehensive org migration guide:
- Side-by-side table: personal account today vs under-an-org
- Bash loop to transfer all 18 repos via gh api
- git remote set-url commands for each local clone
- Post-migration org-level registration token fetch
- Workflow propagation strategies (reusable workflow vs sync script)
§9 (new) — Monitoring + observability:
- GitHub Actions tab per-repo + per-org workflow views
- Runner pool health (Settings → Actions → Runners) at repo + org level
- gh CLI commands for scripted monitoring (run watch, list, view, runners)
- Host-side journalctl + _diag/ inspection commands
§14 Questions — updated to ask about scope (repo vs org) first.
Section numbering shifted by +1 from §9 onward to make room for the
new Monitoring section.
Delegation prompt for the Codex agent running on the Hostinger VM to:
- Install a dedicated GitHub Actions self-hosted runner under gha-runner user
- Register it with saravanakumardb1/learning_ai_common_plat
- Run as a systemd service with auto-start
- Install Node 20 / pnpm 9 / gh CLI / Docker prerequisites
- Wire up local Gitea publish token
Includes full end-to-end validation that proves the actual publish
pipeline works:
- Creates a throwaway @bytelyst/_runner-e2e-test package
- Publishes to local Gitea
- Uploads tarball as GitHub Release asset
- Verifies Gitea registry returns the version
- Verifies pnpm install + require works from a clean directory
- Verifies the byte-identical-tarball invariant (sha256 match between
Gitea-served tarball and GitHub Release asset) — this is the key
guarantee that lockfiles will remain portable across corp Mac's
local Gitea after sync
- Documents cleanup of test artifacts
Plus pre-flight checks, hardening (systemd limits, log rotation,
workflow approval), scaling notes, deliverables checklist, guardrails,
rollback, and follow-up prompt list (publish-packages.yml,
bytelyst-sync script, SKILL doc).
Documents the local zsh chpwd hook + git credential helper that
auto-routes the gh CLI to the personal saravanakumardb1 account
whenever cwd is inside ~/code/mygh/, falling back to the keyring
active account elsewhere.
Captures the setup so it can be reproduced on a new laptop or by
another contributor with similar two-account needs. This is machine-
local config (lives in ~/.zshrc and ~/.gitconfig-personal), so it
intentionally lives in SKILLS/ rather than per-repo AGENTS.md.
Tail-end of the ts-any TODO-4 work uncovered a small class of false
positives the scanner still surfaced: ':any' that appears as TEXT inside
a string literal or JSX child, not as a TypeScript type annotation.
Examples:
const label = 'Energy: any'; // string content, not a type
<Badge>owner:any</Badge> // JSX text, not a type
Real TS ': any' annotations are followed by ',', ')', '=', ';', '>',
or end-of-line. Text occurrences are followed by alphanumeric / quote /
closing-tag delimiter characters \u2014 a clear distinguishing signal.
This commit adds a 10-line regex heuristic that skips occurrences where
':any' is followed by ' ', single quote, double quote, or '<'. The
companion AGENT_COMPLIANCE_ROADMAP.md entry for commit 79041714 already
listed this heuristic; the implementation just wasn't actually committed
at the time. This commit retroactively lands it so the working tree
matches the docs.
Verification: scripts/check-rule-violations.sh still emits 0 findings
across all 20 repos (no regression from the additional heuristic).