Turn Telegram messages into shipped open-source code.
Foundry is an AI-assisted development tool that takes a project from idea → built → tested → deployed → public open source, driven from a Telegram bot on your phone. Each consequential step is human-gated. Every consequential action is audited. Secrets are caught before they leave your machine.
Status: v0.1 engine — feature-complete at the v1.0 milestone (Block 9d Step 6 conformance seal, commit e994c37, 2026-05-20). Full suite: 687 pass / 2 skip / 0 failed. The version number in pyproject.toml is still 0.1.0; "v1.0" is the milestone state of the v0.1 engine, not a semver bump. See The v1.0 milestone below.
Foundry is the successor to Loom — Loom builds throwaway prototypes; Foundry builds persistent projects with multi-cycle development, git history, GitHub publishing, private Tailscale previews, public deploys, and a strict open-source readiness gate.
Fit-for-purpose deployment. Each template type routes to the deploy backend that matches its shape, not a one-size-fits-all platform. The architecture is structured around per-template deploy_backend dispatch (TEMPLATE_REGISTRY in foundry/projects.py). At the v1.0 milestone, Fly.io is wired up for web and python-service templates; nextjs and python-cli decline with sign-posted reasons explaining the right alternative path (Vercel for Next.js, PyPI / GitHub Releases for CLIs). See Templates.
- The v1.0 milestone
- What Foundry does
- Why Foundry exists
- The agentic builder
- What Foundry doesn't do
- Installation
- The verb surface
- Templates
- Security model
- Architecture
- The discipline
- Provenance
- Contributing
- License
- Acknowledgments
The pyproject.toml version is 0.1.0. "v1.0" refers to the milestone of the original 10.5-block Foundry plan having shipped end-to-end, not a semantic-version bump. The milestone consists of:
- Blocks 1–7 — persistent project model, multi-template support, Telegram bot, builder + spawner, git verbs, mid-build interaction, long-running build infrastructure.
- Block 5.5 —
BACKLOG.mdper project + proposed-delta workflow. - Block 6.5 — open-source prevention: gitleaks pre-commit hook + trufflehog history scan +
/auditverb + build-cycle commit blocking on secret detection. - Block 8 —
/shipdeploy verb to Fly.io forwebandpython-servicetemplates. - Block 8.6 (a/b/c) — minimal runtime slice:
/servefor Fly secret injection + persistent volume attachment, scoped to one OAuth token + one append-only state file. - Block 9 (a/b/c/d) — open-source curation:
/releasewith seven subcommands ending inflip-public, the strongest gate in the project. - D.4 — re-register seam closure for the runtime layer.
The v1.0 commit is e994c37 (Block 9d Step 6 — END-TO-END CONFORMANCE SEAL). The full audit trail and lessons file accumulate from there.
You text a Telegram bot. The bot drives an agentic development loop on a headless Mac Mini — building, testing, committing, scanning for secrets, pushing to GitHub, deploying to Fly.io, generating open-source documentation, scrubbing history, flipping public. Each consequential step is human-gated; nothing destructive happens without explicit confirmation; every decision is recorded in an append-only causation-graph audit log.
A complete workflow looks like:
You: /project new dispatch python-service
You: /build a FastAPI endpoint at /publish that posts to LinkedIn
You: /push
You: /approve
You: /ship
You: /ship confirm
You: /release prepare
You: /release license mit
You: /release readme
You: /release docs
You: /release flip-public
You: /release flip-public confirm tombarry-maker/dispatch
Twelve Telegram messages take a project from nothing to live-on-Fly + public-on-GitHub. The bot replies between each step with status, review URLs, and confirmation prompts. The whole loop runs on your phone.
Three reasons, in priority order.
Mobile-first agentic development. Most AI development tooling assumes a desktop with a keyboard. Foundry assumes a phone, a Tailscale-bound Mac Mini doing the work, and a workflow that fits into the gaps in a real life — evenings, weekends, the 20 minutes between meetings. The Telegram surface is deliberately the primary interface, not a notification afterthought.
Persistent projects, not throwaway prototypes. Foundry's predecessor, Loom, builds ephemeral one-off prototypes with hard auto-shutdown. Foundry builds projects: multi-week iteration, real git history, a backlog that compounds, a path from prototype to shipped public software. Loom still works for throwaway builds; Foundry adds the persistent layer above it.
Human-gated security all the way to public. The path from "I have a working prototype" to "this is open source on GitHub" is where most personal projects either never make it (too much friction) or leak secrets (too little friction). Foundry encodes the full readiness gate as a deterministic checklist: secret scanning forward (Block 6.5) and backward through history (Block 9), license generation, README/CONTRIBUTING/CODE_OF_CONDUCT/SECURITY templates, confidentiality denylist, personal-info scanning, and a typed-name confirmation defense against chained-paste mistakes on the irreversible public flip. The strongest gate in the project is the one that says "this is now public," and it earned its strength deliberately.
Foundry doesn't call the Anthropic API directly. There is no anthropic SDK dependency in pyproject.toml. The /build verb spawns a subprocess of Claude Code (the claude CLI binary), which runs against your Claude Max subscription. This is a deliberate architectural choice: Claude Code already handles model dispatch, streaming-output protocol, budget enforcement, and the agentic loop — Foundry composes it rather than reimplementing it.
The exact invocation lives in builder/run.py:
cmd = [
"claude",
"-p",
"--model", "sonnet",
"--output-format", "stream-json",
"--verbose",
"--permission-mode", "bypassPermissions",
"--max-budget-usd", str(governance.MAX_SPEND_USD),
"--no-session-persistence",
"Read your CLAUDE.md and proceed.",
]Five things worth understanding:
The model alias, not a pinned version. --model sonnet is the alias; Claude Code resolves it to whatever the current Sonnet model is at spawn time. As of v1.0 (May 2026), that's Claude Sonnet 4.6. When Anthropic releases a new Sonnet, Foundry builds use it automatically without code changes. The audit log captures whatever model string the CLI reports in its system/init event, so the actual model used per build is recoverable from the log via event="builder_start". The choice of Sonnet over Opus is deliberate cost/capability balancing — Sonnet 4.6 handles structured, well-spec'd agentic build work effectively at a fraction of Opus's cost; Opus would be overkill for most builds.
Cost math vs. cost cap are different things. builder/run.py carries pricing constants pinned to Sonnet 4.6 standard-tier (input $3/M tokens, output $15/M, cache-read $0.30/M, cache-5m $3.75/M, cache-1h $6/M) for token-cost forecasting and audit-log accounting. The actual spend cap is --max-budget-usd enforced by Claude Code itself. If sonnet resolves to a model with different pricing, the forecasts drift; the cap doesn't.
Permission bypass is deliberate. --permission-mode bypassPermissions means the builder doesn't pause to ask permission for individual file operations. This is the mode that makes overnight unattended builds possible — Foundry compensates for the lack of fine-grained per-file gating with structural defenses: the workspace is sandboxed to ~/foundry/projects/<slug>/, the subprocess env is scrubbed of credentials by foundry/env.py (so the builder cannot reach Anthropic, GitHub, or any other authenticated service of its own accord), all commits go through the pre-commit secret scanner, and gated verbs (/ship, /release flip-public) require explicit human confirmation outside the build loop. The tradeoff is intentional: high build velocity, with the security perimeter drawn at the network and release boundaries rather than at each file write.
Sessions don't persist across builds. --no-session-persistence means each /build starts fresh — no Claude Code conversation memory carries over. The persistent project state lives in BACKLOG.md (cross-build task tracking), CLAUDE.md (project-specific build context), and the git history (commits with full context). The builder reads these on each spawn; it doesn't remember the last conversation. This is the architectural commitment behind cross-build state being filesystem-grounded rather than session-grounded.
The prompt is one sentence. The actual prompt is "Read your CLAUDE.md and proceed." All the substantive instruction lives in the per-project CLAUDE.md (template-scaffolded, then evolved by you) and BACKLOG.md (the build-cycle backlog). The builder doesn't get verbose instructions; it gets context. The discipline is to make that context complete enough to drive correct behavior — there is no fallback prompt-engineering layer to compensate for incomplete CLAUDE.md.
If you want to see what model a specific build actually used, grep the audit log for event="builder_start" and read the model field — Claude Code reports its resolved model in the system/init event and Foundry captures it.
Foundry is honest about its scope. The following are deliberately out of scope:
- Heavy backends. No relational database scaffolding, no schema migrations, no ORM integration. Foundry deploys stateless application code plus a minimal runtime slice (one OAuth token via Fly secrets, one persistent volume). If you need crash-safe relational data, multi-user auth, or scheduled background work, Foundry is not the right tool.
- Multi-machine orchestration. Foundry runs on a single Tailscale-bound Mac Mini. There is no cluster mode, no remote worker pool, no cloud-deployment of Foundry itself.
- Multi-user operation. Foundry is single-user by design (one
TELEGRAM_CHAT_IDwhitelisted at startup). It is not a SaaS, not a team tool, and has no concept of authentication beyond "is this message from the whitelisted chat." - Vercel deployment. Templates that need Vercel (
nextjs) decline/shipwith a sign-posted reason. TheTemplateSpec.deploy_backendfield structurally accommodates additional backends, but only Fly.io is wired at v1.0. The decline reason directs you to import the repo at vercel.com/new manually. - PyPI / GitHub Releases distribution. Templates with no deploy URL (
python-cli) decline/shipwith a sign-posted reason. Useuv build+uv publish(PyPI) orgh release create(GitHub Releases) directly. - Heavy CI/CD integration. No GitHub Actions templates, no auto-release-notes, no semver-bumping automation.
/releasegenerates the docs scaffolding; you wire CI yourself if you want it. - Flip-back-to-private. The
/release flip-publicverb is one-way. If you need to flip a repo back to private, rungh repo edit --visibility privatedirectly.
If these gaps would block your use case, Foundry isn't the right tool for that work — and that's fine. The architecture is shaped to be honest about what it does, not to pretend to be everything.
Foundry is designed to run on a single Tailscale-bound Mac Mini as the headless automation host, with Telegram as the user-facing surface.
- macOS with Apple Silicon (tested on M4 Pro, 64GB / 2TB). Linux may work; not tested.
- Python 3.12+ (per
pyproject.tomlrequires-python = ">=3.12"). - uv for Python package management.
- git (system git is fine).
- git-filter-repo for history scrubbing. Install via
uv pip install git-filter-repo(no setup script — install manually). - Tailscale for remote access and
/previewURLs. - gh CLI authenticated to your GitHub account (
gh auth login). - flyctl authenticated to your Fly account (
fly auth login). Installed bysetup_deploy_tools.sh. - gitleaks 8.30.1 and trufflehog 3.95.3 binaries on PATH. Installed by
setup_scanning_tools.shto~/.local/bin/(versions pinned in the script; bump deliberately). - A Telegram bot token (BotFather) and your Telegram chat ID.
- An Anthropic API key for the builder subprocess.
# Clone
git clone git@github.com:tombarry-maker/foundry.git
cd foundry
# Install secret-scanning binaries (gitleaks 8.30.1 + trufflehog 3.95.3 → ~/.local/bin/)
sh setup_scanning_tools.sh
# Install deploy tooling (flyctl 0.4.52 → ~/.local/bin/)
sh setup_deploy_tools.sh
# Install Python dependencies
uv sync
# Install git-filter-repo for /release scrub-history
uv pip install git-filter-repo
# Run the full test suite (expect 687 passed / 2 skipped / 0 failed at v1.0)
uv run pytest -qCreate a .env file at the repo root with these three variables:
ANTHROPIC_API_KEY=sk-ant-...
TELEGRAM_BOT_TOKEN=...
TELEGRAM_CHAT_ID=...ANTHROPIC_API_KEY— for the builder subprocess. Scrubbed from all child processes byfoundry/env.pybefore the build subprocess runs (the builder cannot see it; it talks to Anthropic through Claude Code's own auth context).TELEGRAM_BOT_TOKEN— from @BotFather.TELEGRAM_CHAT_ID— your numeric Telegram user ID (Foundry is single-user by design).
.env is gitignored. Never commit it.
Optional environment variables:
FOUNDRY_AUDIT_LOG— override the default audit log location. Used by tests to redirect to a tmp path.FOUNDRY_SKIP_SCANNING=1— TEST-ONLY. Set bytests/conftest.pyat module level to bypass the pre-commit hook during the test suite. Production must NEVER set this variable.
Create ~/.foundry/global-config.toml with your default copyright holder (used by /release license):
[release]
copyright_holder = "Your Name"
preferred_license = "mit"Foundry never holds these tokens — they live in their respective CLI's auth context:
gh auth login # for GitHub remote creation and public flips
fly auth login # for /ship and /serve deploysFoundry runs as a long-lived bot process. Recommended pattern is a tmux session that survives SSH disconnect:
tmux new -s foundry
cd ~/foundry
uv run python -m bot
# Detach with Ctrl-b d; reattach later with: tmux attach -t foundryYou're now connected. Open Telegram, find your bot, and try /project list.
Foundry's complete command surface as of v1.0 — 18 top-level Telegram commands. Each verb is a Telegram command. Subcommands are space-separated arguments. Verified against bot/handlers.py at HEAD e994c37.
/project new <slug> [template] Create a new project workspace. Templates:
web | python-service | python-cli | nextjs.
Scaffolds template files, BACKLOG.md,
.foundry/release-config.toml, .gitleaks.toml,
fly.toml (web/python-service only), git init,
pre-commit hook install.
/project list List all projects with current status.
/project switch <slug> Set the active project for this chat.
/project current Show the active project for this chat.
/project archive <slug> Mark a project archived. Workspace stays;
won't appear in default /project list.
/project remote [<url>] Show or set the GitHub remote URL.
No url → show current.
/project create-remote <user/repo> Create the GitHub remote via gh CLI
(private by default; flip via /release
flip-public). Sets origin and pushes
initial main branch.
/build Spawn an agentic build subprocess against
the active project's workspace. Bare /build
reads the top "Up next" item from BACKLOG.md.
If that item is shorter than 80 chars,
Foundry replies with a /build confirm
short-spec confirmation prompt rather than
spawning immediately.
/build confirm Confirm the short-spec /build proposal.
/build <spec text> Spawn with the explicit natural-language
spec. CLAUDE.md and BACKLOG.md are injected
as persistent context.
/build auto Same as bare /build (explicit auto-spec).
/iterate <feedback> Continue the most recent build with new
feedback. Preserves session state across
Foundry restarts.
/status Show the current build's progress: turn
count, elapsed time, last milestone,
pending commit. Refreshes from build session.
/tail <n> Show the last N lines of the build's
streaming output. Default 20.
/quiet Mute build milestone notifications for the
current session.
/unquiet Resume build milestone notifications.
/pause Pause the build subprocess (Block 7.3).
Caps freeze; state persists.
/resume Resume a paused build.
/kill Abandon the current build. Two-step
confirmation: bare /kill replies with the
current state; /kill confirm actually
terminates. Confirmation expires after
60 seconds (KILL_CONFIRMATION_TIMEOUT_SECONDS).
/kill confirm Confirm the kill within 60 seconds.
/kill cancel Cancel the pending kill.
/kill -y [reason] Force-kill without confirmation (escape hatch
for stuck builds).
/approve Approve the build's pending commit + push.
Used by /push's two-step flow.
Build caps default to 12 hours pause-aware wall-clock / 500 turns / $100 per session (Block 7's long-running infrastructure). The build state survives Foundry restarts via .foundry/build-session.json.
BACKLOG.md lives at the root of each project and is the single source of truth for project state outside the audit log. Build cycles read it as injected context and propose deltas as part of their output, which you accept, reject, or edit.
/backlog Same as /backlog show.
/backlog show Show the full current backlog.
/backlog status Show only Status + In progress + top 3 Up next.
/backlog add <text> Append to "Up next".
/backlog add-priority <text> Insert at position 1 of "Up next".
/backlog start <item-id> Move "Up next" → "In progress".
/backlog done <item-id> Move "In progress" → "Done" with commit/
build attribution.
/backlog defer <item-id> Move "In progress" → bottom of "Up next".
/backlog block <item-id> <reason> Move to "Blocked" with reason.
/backlog unblock <item-id> Move "Blocked" → "Up next".
/backlog decision <text> Add to "Decisions made" with current date.
/backlog question <text> Add to "Open questions".
/backlog answer <q-id> <answer> Resolve open question + auto-create a
"Decisions made" entry.
/backlog oos <text> Add to "Out of scope (deliberately)".
/backlog accept-delta Apply a build's proposed delta verbatim.
/backlog reject-delta [reason] Discard a proposed delta.
/backlog show-delta Display the pending delta text.
/backlog edit-delta Get the delta text for inline editing
before applying.
/backlog edit Reply with the file path; edit BACKLOG.md
directly in your editor of choice.
/backlog history Last 10 backlog modifications with
attribution.
Item IDs are short-form ([U1], [B1], [D3]) generated by Foundry and stable until the item changes section.
Block 6.5 prevents secrets from leaking forward; Block 9's scrub-history (below) cleans up past commits. The /audit verb is the human-readable check.
/audit secrets Run gitleaks against working tree + staged
changes. Read-only. Reports findings with
file:line + first 8 chars of the secret
redacted.
/audit secrets-history Run trufflehog against the entire git
history with credential verification.
Slower (30s–5min). Read-only.
/audit gitignore Verify .gitignore covers the Foundry
baseline. Reports gaps.
The pre-commit hook fires automatically on every commit (including build-cycle commits). A detected secret pauses the build with three options:
/build resolve fix— you fix the secret manually; the next/iterateretries/build resolve allow— false positive; Foundry adds an inline allowlist comment and proceeds/kill— abandon the build entirely
Foundry-generated commits never bypass the hook. --no-verify does not appear anywhere in Foundry's commit logic.
/push Push the active project's main branch to
its GitHub remote. Stages the push and
waits for /approve.
/approve Approve the pending push.
/preview Start a Tailscale-bound preview of the
project. Returns a private URL only your
Tailscale network can reach.
/preview stop Stop the preview.
/preview works for any template that can run a dev server (web, python-service, nextjs); python-cli has no preview by design.
/ship Deploy the current main branch SHA to its
template's deploy backend. At v1.0, Fly.io
is wired for web and python-service;
nextjs and python-cli decline with
sign-posted reasons. See Templates above
for the full dispatch table.
Two-step: bare /ship stages and shows the
readiness summary; /ship confirm performs.
Refuses to ship code that isn't pushed
(sync precondition).
Confirmation does NOT expire (SP-1 LOCKED).
/ship confirm Perform the deploy.
/ship cancel Cancel the pending deploy.
/serve register [--yes] Register the project's Fly app for the
Block 8.6c runtime layer (secret injection
+ persistent volume). Two-step via --yes
flag.
/serve unregister [--yes] Unregister the runtime layer.
/serve start Start the registered runtime.
/serve stop Stop the registered runtime.
/serve status Show registered runtime state.
The Fly API token never enters any Foundry artifact. flyctl owns its own auth context (~/.fly/). /ship calls flyctl auth whoami as a pre-flight (identity, not token) and refuses cleanly if you're not authed.
The capstone block, and the strongest gate in the project. Seven subcommands take a project from "private repo with a working build" to "public on GitHub with a proper open-source posture."
/release prepare Run the full pre-flight checklist
(13 readiness checks). Read-only. Reports
PASS/FAIL per check with actionable
symptoms on failures.
/release status Quick summary of release readiness.
/release license [id] Add LICENSE file. Supported:
mit | apache-2.0 | bsd-3 | gpl-v3.
Bare /release license uses
.foundry/release-config.toml's
preferred_license. Replace-existing
requires confirm.
/release readme [--overwrite] Generate/regenerate README.md from a
template + project state. Marker-anchored
(FOUNDRY:README_GENERATED) — your edits
below the marker are preserved across
regenerations. --overwrite forces
regeneration of a README that has no
marker.
/release docs [--overwrite] Generate companion files:
CONTRIBUTING.md, CODE_OF_CONDUCT.md
(Contributor Covenant v2.1), SECURITY.md,
.github/ISSUE_TEMPLATE/*, PULL_REQUEST_
TEMPLATE.md. Same marker-anchored
preservation as readme.
/release scrub-history [--yes] Remove a pattern from the entire git
history via git-filter-repo. DESTRUCTIVE.
--yes flag confirms; without it, the verb
lists matches and refuses. Always creates
a backup branch. Manual force-push is your
responsibility (Foundry does not
auto-force-push).
/release flip-public Flip the GitHub repo from private to
public via gh CLI. Two-step with the
strongest confirmation in the project:
bare verb renders the gate; confirm
requires the typed nameWithOwner.
/release flip-public confirm <nameWithOwner>
Perform the flip. The typed nameWithOwner
must match the staged repo name EXACTLY
(case-sensitive, no normalization).
/release flip-public cancel Cancel the pending flip.
A bare /release flip-public evaluates four hard pre-flip conditions:
/release preparepassed against the current HEAD. Stale prepare runs fail. The bound isrelease-state.json'sgit_head_sha == HEAD.- Scrub-history was evaluated for this project. Either a
scrub_history_succeededevent (secrets found and removed) or ascrub_history_noopevent (clean check) must exist in the audit log for this project's slug. "Never ran" fails — the question must have been posed. - Fresh secret scan returns zero findings. Run live at gate time; not a cached result.
- A LICENSE file exists at the workspace root.
If any condition fails, the gate refuses with specific actionable refusal naming each failed ordinal and its symptom. If all four pass, Foundry stages a pending entry and renders this gate message verbatim (sample with project integ-test and repo tombarry-maker/integ-test):
/release flip-public staged for integ-test.
REPO: tombarry-maker/integ-test (currently PRIVATE)
TARGET: PUBLIC
Pre-flip checklist (D.5):
(1) prepare passed — [PASS] HEAD abc1234
(2) scrub-history ran — [PASS] last: succeeded
(3) zero scan findings — [PASS] fresh
(4) license exists — [PASS] LICENSE present
!! THIS IS IRREVERSIBLE AT THE PUBLIC LAYER !!
The gh CLI will run:
gh repo edit tombarry-maker/integ-test --visibility public --accept-visibility-change-consequences
Consequences (per gh upstream --help, verbatim):
- Losing stars and watchers, affecting repository ranking
- Detaching public forks from the network
- Disabling push rulesets
- Allowing access to GitHub Actions history and logs
This is a HARD HUMAN GATE — confirmation does NOT expire (SP-1 LOCKED). The confirm sub-verb requires the operator to TYPE the repo's full name as anti-chained-paste friction — no bare `confirm`.
To proceed: /release flip-public confirm tombarry-maker/integ-test
To cancel: /release flip-public cancel
The typed nameWithOwner is the chained-paste defense. The project's design spec recognizes that the identical slip on flip-public is permanent and unrecoverable, so confirmation cannot be a bare /release flip-public confirm (which a chained paste could trigger). Instead, you must type the exact repo name the gate just rendered — case-sensitive, no normalization, no case-folding. The defense is bound by a regression-oracle test that plants a case-different input and asserts refusal.
Once you confirm with the matching typed name, Foundry mints an authorization token, logs a CRITICAL flip_public_confirmed audit event (the human-authorization moment, threaded into the causation chain), and invokes gh repo edit with both flags. The repo is now public.
There is no flip-back-to-private verb. If you need it, run gh repo edit --visibility private directly.
Foundry ships four templates. Choose at /project new <slug> <template>. Verified against TEMPLATE_REGISTRY in foundry/projects.py:55-110:
| Template | Shape | Deploy backend | Status at v1.0 |
|---|---|---|---|
web |
FastAPI + HTMX + Jinja2 server-rendered Python app | Fly.io | Shipped. /ship deploys via flyctl. |
python-service |
FastAPI JSON API + uvicorn | Fly.io | Shipped. /ship deploys via flyctl. |
python-cli |
Command-line tool, no web surface | — | Declines /ship. A CLI has no server, no URL, no health check. Decline reason directs to uv build + uv publish (PyPI) or gh release create. Your source is already published via /push. |
nextjs |
Next.js application | — | Declines /ship. Vercel owns the Next.js framework and has first-party support for it; Fly is the wrong platform. Decline reason directs to vercel.com/new manual import (your GitHub repo already exists via /push). |
The deploy backend is not a global Foundry setting; it's a per-template field on TemplateSpec:
TemplateSpec(
directory="...",
required_paths=(...),
deploy_backend="fly", # or None (no deploy at v1.0)
decline_reason="..." # required if deploy_backend is None
)on_ship reads project.template_type, looks up the TemplateSpec, and dispatches:
deploy_backend == "fly"→flyctldeploy path.deploy_backend is None→ decline with the embeddeddecline_reason(no half-served deploys, no opaque errors).
This is fit-for-purpose deployment as architecture, not as configuration. The "missing deploy entry" bug class is structurally impossible: a template with deploy_backend=None must also have a decline_reason (a decline_reason is the explicit acknowledgment that this template's deploy path is something other than Fly). The architecture is shaped to accommodate per-template backend dispatch; v1.0 ships one (Fly).
- Source layout (
app/main.py, package structure, etc.) pyproject.tomlwith pinned dependencies (Python templates).gitignorewith the security baseline (env files, credentials, audit dir).gitleaks.toml(default rules, fromtemplates/_shared/).foundry/scanning-config.toml(custom-pattern slots, fromtemplates/_shared/).foundry/release-config.toml(license preference, copyright holder, denylist, allowlist)BACKLOG.mdwith template-appropriate starter contentCLAUDE.mdwith project-specific build-cycle contextfly.toml(web + python-service only — required for/ship)- Pre-commit hook (gitleaks)
Foundry's security model is built around three principles:
Tokens never live in Foundry. gh, flyctl, and the Telegram bot token are the three external secrets the system uses. The first two are owned by their respective CLI auth contexts (~/.config/gh, ~/.fly/); Foundry never reads, copies, or persists either. The Telegram bot token, the Telegram chat ID, and the Anthropic API key live in .env (gitignored). foundry/env.py actively scrubs ANTHROPIC_API_KEY, GH_TOKEN, and GITHUB_TOKEN from every build subprocess's environment before the subprocess starts — the builder subprocess cannot see those values; it talks to Anthropic through Claude Code's own auth context, and it cannot push to GitHub directly (only Foundry's main process holds the privilege to invoke gh). No token of any kind appears in any audit event, any log file, any persisted state, any commit, or any subprocess captured-output that gets persisted.
Scanning is bidirectional. Block 6.5 prevents leaks going forward (pre-commit hook on every Foundry-generated commit; build-cycle commit blocking on secret detection). Block 9 cleans up past leaks (trufflehog history scan, scrub-history wrapper around git-filter-repo with mandatory backup branch). The gate that lets a project go public requires both directions to be green.
Irreversible actions are human-gated with structural defenses, not just confirmation prompts. The flip-public gate's typed-nameWithOwner confirmation is not a UX nicety — it's a deliberate defense against the specific failure mode of chained-paste mistakes on permanently-unrecoverable actions. The same chained input that would trigger a bare confirm cannot trigger a confirm that requires typing the just-rendered repo name. The defense is encoded as exact string equality with no case-folding, bound by oracle tests that fire on case-fold regression.
The audit log itself is the system's forensic backbone. Every consequential event has a UUID, a parent_uuid pointing back to its causal antecedent, a project_slug, a timestamp, and a structured data payload. The chain runs from project_create through every build, push, deploy, audit-scan, and release event. CRITICAL events (ship_confirmed, ship_started, ship_succeeded, flip_public_confirmed) mark human-authorization moments and are queryable by event type. The log is append-only JSONL — no update or delete operations exist in the codebase.
Verified by git grep across bot/ foundry/ governance/ remote/ at HEAD e994c37:
flip_public_ (8 events):*
flip_public_requested— bare/release flip-publicstaged a pending entryflip_public_confirmed— CRITICAL — confirm path with matching typed name, human-authorization momentflip_public_refused— confirm refused (typed-name missing, mismatch, or project drift)flip_public_declined—/release flip-public cancelinvokedflip_public_precondition_failed— bare verb refused on failing checklistflip_public_noop— bare verb refused because repo is already PUBLICflip_public_failed— wrapperflip_repo_visibility_public()raisedflip_public_succeeded— wrapper succeeded (logged fromremote/github.py)
ship_ (8 events):*
ship_requested— bare/shipstaged a pending entryship_confirmed— CRITICAL — human-authorization momentship_started— CRITICAL —flyctl deployinvokedship_succeeded— CRITICAL —flyctl deployexited 0ship_declined—/ship cancelinvoked, or template declined (nextjs/python-cli)ship_refused_unsynced— sync precondition failed (workspace ahead of origin)ship_failed—flyctl deployraisedship_state_persist_failed—deploy-state.jsonwrite failed (ambiguous-outcome path)
scrub_history_ (4 events):*
scrub_history_succeededscrub_history_noopscrub_history_refusedscrub_history_failed
backlog_ (5 events):*
backlog_createbacklog_delta_proposedbacklog_delta_appliedbacklog_delta_rejectedbacklog_delta_none
Plus the Block 6.5 scanning events (gitleaks_scan_complete, trufflehog_scan_complete, gitignore_audit, secret_finding_detected, secret_resolved_fix, secret_resolved_allow, build_paused_for_secrets), the Block 4 build events (build_spawn, builder_complete, build_pause, build_resume, build_kill_*), and project lifecycle events (project_create, project_archive).
If you want to understand a specific decision, trace its parent_uuid chain backward to the root. You will find every decision that led to it, in chronological order, with full structured payloads.
foundry/ Core library
__init__.py
audit.py Append-only JSONL audit log writer
authorization.py SubBlockAuth factories, _FACTORY_IMPORT_ALLOWLIST
backlog.py BACKLOG.md parsing, delta application
deploy_state.py /ship deploy-state persistence
env.py Token scrubbing, GIT_CONFIG_GLOBAL isolation,
bot_token() and chat_id() accessors
projects.py TEMPLATE_REGISTRY, create_project, project state
release.py FlipPublicChecklist + 4-condition engine +
license/readme/docs generation
runtime.py Block 8.6c /serve runtime layer
scanning/ Block 6.5 secret-scanning package
session_state.py Build-session state persistence
state.py ActiveState (pending_* fields per gate verb)
bot/ Telegram bot
__init__.py
__main__.py Entry point (uv run python -m bot)
event_tee.py
handlers.py All 18 verbs registered as CommandHandlers
startup.py
builder/ Agentic build subprocess orchestration
run.py
spawner/ Build-spawn context injection (CLAUDE.md +
BACKLOG.md → builder subprocess)
inject.py
remote/ External CLI wrappers
fly.py flyctl wrappers (Block 8)
git_filter_repo.py scrub-history wrapper (Block 9)
github.py gh CLI wrappers (create_remote_via_gh,
read_repo_visibility,
flip_repo_visibility_public, push,
decide_sync)
launchctl.py Block 8.6c launchd integration
preview/
tailscale.py Tailscale-bound preview discovery
governance/ Cross-cutting policy
release.py Release-flow orchestration
templates/ Project scaffolding
_shared/ Cross-template assets (.gitleaks.toml,
.foundry-scanning-config.toml)
web/ FastAPI + HTMX + Jinja2
python-service/ FastAPI JSON
python-cli/ CLI tool
nextjs/ Next.js
tests/ 56 test files
conftest.py Shared fixtures (tmp_audit_log,
FOUNDRY_SKIP_SCANNING=1)
test_block_*.py Per-block unit/integration test files
audit/ Audit log directory (gitignored)
docs/ Project documentation (plans, lessons)
Top-level configuration files: .env (gitignored), .gitignore, pyproject.toml, uv.lock, CLAUDE.md (build-time agent context), BACKLOG.md, README.md (this file), how-to-work-with-this-repo.md, setup_scanning_tools.sh, setup_deploy_tools.sh.
The architecture is intentionally flat. Each block ships a contained surface (one or two new modules, additive edits to existing ones), with the AR-mitigation reviewable-diff pattern preventing structural drift: any module that imports a sub-block authorization factory (for_ship_confirm, for_scrub_history_confirm, for_flip_public_confirm) must be on the factory's allowlist, with the import and the allowlist update landing as an atomic reviewable diff in the same commit.
Foundry is built to be debuggable over magical. When something goes wrong, you can trace it: the audit log gives you the causation chain; the build session JSON gives you the agentic build's full state; the pending-state fields on ActiveState tell you exactly what's awaiting confirmation; the BACKLOG.md tells you what the project thought it was doing.
A few things about how Foundry was built that show up in how it works.
State-not-narrative verification. Every load-bearing claim is verified against literal bytes on disk rather than paraphrased from prior context. If you ask /audit secrets, the bot runs the scan and reports the actual output, not a summary of what the scan probably would have said. The build log explicitly records seven inversions (cases where an assertion was made about the codebase without first verifying it against disk); each one is a reminder that the practice of re-grounding from disk before asserting is non-negotiable on a project that gates irreversible actions on disk state.
Sanity-pair test discipline. Every positive assertion is paired with its negative counterpart. test_flip_public_with_all_green_proceeds ↔ test_flip_public_requires_prepare_all_green. test_scrub_history_creates_backup_branch ↔ test_scrub_history_dry_run_does_not_create_backup. The test suite at v1.0 is 687 passing / 2 skipped / 0 failed, with every regression oracle bound to a specific behavioral property rather than a happy-path assertion.
Smoke gate every block. Each block ships with its smoke gate before the next begins. Block 1 shipped with a real bug already caught and fixed by its smoke gate: an order-of-operations issue where project metadata was written after the initial git commit, leaving the metadata file untracked. This is the kind of thing the discipline catches in five minutes that survives a year in undisciplined code. The audit/lessons trail accumulates from there.
Bank-and-resume at high-stakes boundaries. Long agentic sessions accumulate cognitive load; high-stakes work (the irreversibility-class crossing in Block 9d, the long-running build infrastructure of Block 7) was deliberately opened in fresh sessions with full attention. This is encoded in the build log; it shows up in the commit messages.
Atomic AR-mitigation diffs. When a new module first imports a privileged factory, the import and the allowlist update land in the same commit. The §5 enforcing test verifies callsite ∈ allowlist via membership-not-equality, so adding a permitted importer requires the atomic diff to stay atomic — split into two commits, the test fails between them.
Comprehension paces velocity. The work isn't yours if you can't explain what it does. Velocity that outruns comprehension is the failure mode the discipline most aggressively guards against.
Foundry was scaffolded the same evening Loom shipped — May 14, 2026.
Loom Ephemeral prototypes — one-off, throwaway by default. [shipped May 2026]
Foundry Persistent single projects — multi-cycle, git history, [v1.0 May 20, 2026]
/push, /preview, /ship, /serve, /release.
Each step up the abstraction ladder composes the prior; it doesn't replace it. Loom still works for ephemeral one-off prototypes. Foundry is the right tool when a build is worth keeping.
- Block 1 (May 14, 2026) — Root scaffolding + persistent project model. First commit. Smoke gate caught and fixed the metadata-after-commit bug.
- Block 5.5 —
BACKLOG.mdper project + proposed-delta workflow. Inserted into the plan after Block 5, when the need for cross-build state became clear. - Block 6.5 — Open-source prevention. Split from Block 9 when the prevention/cleanup distinction became architectural.
- Block 9d (commits
18f850dthroughe994c37, May 18–20, 2026) — Open-source curation's irreversibility-class subblock. The capstone. Six gated commits across six planned steps, with the chained-paste defense bound by oracle tests and the AR-mitigation atomic diff active. v1.0 sign-off ate994c37.
Foundry is currently a single-operator project. Public contribution flows are not yet defined.
If you find a bug or have an idea, open an issue. If you want to discuss the architecture, open a discussion. Pull requests are welcome but expect a high bar — see CONTRIBUTING.md for contribution guidelines and CODE_OF_CONDUCT.md for the participation policy.
See SECURITY.md for the vulnerability reporting process.
MIT — see LICENSE. Copyright © 2026 Signal + Pattern LLC.
Foundry was built between May 14 and May 20, 2026 on a Mac Mini M4 Pro, primarily on weekends, primarily by Telegram. The discipline of state-not-narrative verification, sanity-pair test pairing, atomic AR-mitigation diffs, and bank-and-resume at high-stakes boundaries was developed across the build and is documented in the project's audit log and lessons file.
Loom, Foundry's predecessor, shipped May 14, 2026 and is still the right tool for ephemeral one-off prototypes.