Skip to content

feat!: rebuild Facility around persistent story workspaces - #289

Draft
adrian-lorenzo wants to merge 3 commits into
mainfrom
feature/persistent-story-workspaces
Draft

feat!: rebuild Facility around persistent story workspaces#289
adrian-lorenzo wants to merge 3 commits into
mainfrom
feature/persistent-story-workspaces

Conversation

@adrian-lorenzo

@adrian-lorenzo adrian-lorenzo commented Sep 2, 2026

Copy link
Copy Markdown
Member

Purpose

Facility 0.12 changes the unit of work from a short-lived run to a durable workspace attached to a story. The story, conversation, Git worktree, native Claude Code and Codex sessions, development services, previews, commits, and pull request share one lifecycle.

Compute may sleep or be replaced. The workspace remains until a maintainer explicitly deletes it. Merging or archiving a story does not delete its worktree, local data, conversation, or engine sessions.

Product behavior

  • Starting the same issue or pull request reuses its existing story workspace instead of creating another run.
  • A workspace can clone every repository connected to the project, create branches and commits, push changes, and open or update pull requests.
  • Repository setup and start commands come from .facility.yml. Docker and Docker Compose run inside the workspace without mounting the host Docker socket.
  • Declared services are exposed through authenticated, project-scoped preview URLs. HTTP and WebSocket traffic use the same access boundary.
  • Claude Code and Codex run through native resumable sessions. Later turns continue from the same files and conversation history.
  • Suspension, merge, archive, disabled schedules, and failed turns retain durable state. Explicit deletion is isolated, authorized, idempotent, and removes only the selected workspace.
  • Failed engine sessions are preserved for diagnosis and marked corrupt. Retrying creates a replacement session in the same worktree instead of silently replaying a turn that may already have changed GitHub.

Agents as code

.agents/ is the source of agent configuration. Kickstart, the API, MCP, the scheduler, and the UI use the same strict parser, normalized representation, and content hash.

Kickstart installs six agents:

  • architect
  • builder
  • pr-reviewer
  • address-review
  • ci-doctor
  • security-audit

Each manifest declares its prompt, engine, model, enabled state, and manual, GitHub, or schedule triggers. Permission, sandbox, tool allowlist, and max_turns fields are rejected. Every agent receives the same full workspace access and a short-lived GitHub App installation token with the configured maintainer capability. GitHub and scheduled triggers use the same dispatcher as MCP and the UI.

Agent changes made in the UI are proposed on a branch and pull request. Repeated requests are idempotent, later edits advance the open pull request without force-pushing, and stale branches are rejected explicitly.

Interfaces

The control service hosts the MCP endpoint and OAuth flow. The deliberately small initial MCP contract contains 13 tools for discovering projects and agents, starting and continuing stories, inspecting their conversation and workspace state, managing previews and compute, and removing a workspace explicitly.

The Facility UI remains supported. It uses the same application services for project setup, agent configuration, stories, conversations, service status, preview access, archive, restore, attention items, and deletion.

Manual, MCP, UI, GitHub, and scheduled work all enter the same turn dispatcher. A second message is queued behind the active turn and is promoted after success, failure, cancellation, or stale-worker recovery.

Security boundary

This release removes receipts, approval workflows, budget enforcement, delivery brokers, and per-agent permission profiles. Agents are trusted repository maintainers inside an isolated workspace.

Authentication, project membership, tenant isolation, webhook signatures, secret handling, preview authorization, branch protection, and control-plane isolation remain enforced. Credentials are short-lived and injected per turn. Project repositories can request only explicitly configured, project-namespaced environment values; they cannot read arbitrary Facility process environment variables. Preview handoffs are single-use, expire, and are revoked when the workspace is suspended or deleted.

0.12 cutover

The package set and generated SDK are versioned at 0.12.0. The legacy run, receipt, HITL, proposal, budget gateway, watchtower, database-backed agent catalog, sandbox, and separate preview paths no longer ship.

0.12 uses a clean database schema. The migration command detects a 0.11 runs table and exits before creating a migration ledger or changing schema objects. The upgrade guide documents backup, export, clean installation, and rollback to an untouched 0.11 database.

Persistent storage is an operator concern. Local conformance tests use a deterministic export-and-restore fixture; hosted installations should use the workspace provider's durable volumes, snapshots, and backup facilities.

Local acceptance evidence

  • pnpm verify passed after the final changes. It covered linting of 200 files, all 14 typecheck tasks, all eight clean build tasks, isolated database recreation, unit and deterministic integration suites, unused-code detection, pinned-action and Markdown-link guards, and the repository audit policy.
  • The script suite passed 114 tests. Package suites passed 7 agent tests, 8 core tests, 10 SDK tests, 32 web tests, 14 MCP tests, and 2 documentation tests. Every API suite selected by the verification script passed.
  • The runner image built locally as facility-runner:dev.
  • DATABASE_URL=postgres://facility:facility@localhost:5461/facility_ws FACILITY_E2E_DOCKER=1 FACILITY_WORKSPACE_TEST_IMAGE=facility-runner:dev pnpm --filter @facility/api test:e2e-workspace passed: 2 files and 3 tests in 51.13 seconds.
  • The Docker-backed tier started a real nested Docker Compose application, API, and database; exercised authenticated and unauthenticated previews in Chromium; replaced compute; and verified that the dirty worktree and native session files survived.
  • Browser checks covered local sign-in, the six-agent catalog and editor, agent validation, story creation, workspace controls, conversation and artifacts, deletion, and the narrow viewport. The browser reported no runtime errors.
  • Cleanup checks found no remaining facility-ws-* containers or volumes.
  • GitHub CI passed on 46ba961: commit and pull-request-title guards, Node 22.13 compatibility (1m40s), release-shaped verification (2m54s), self-hosted image build (10m22s), and Docker-backed workspace E2E (12m23s). All release allocation and publication jobs were skipped.

No live repository was mutated during local acceptance testing. The private deployment was not changed. This pull request remains a draft, has not been merged, and no package or container image has been published.

Release gates still open

These checks must be completed before this PR is ready for review or merge:

  • Reauthenticate the TAM-OS MCP connection and compare the implementation against the full strategy transcript for meeting b8e82829-0940-48fb-9f66-4503906fd3f4.
  • Add or authorize a protected candidate deployment path. The existing private production workflow accepts only commits already contained in public main, so it correctly refuses this draft PR SHA.
  • Run real Claude Code and Codex sessions with hosted engine credentials and verify resume after compute replacement.
  • Run the reference journey against a non-production GitHub App installation: start from MCP, continue from the UI, push a branch, create or update a pull request, and exercise GitHub and scheduled triggers.
  • Open an authenticated remote preview from a second computer.
  • Complete the 14-day retention soak required by the epic.

Reviewer checklist

  • Start one issue from MCP and continue it from the UI.
  • Replace or suspend compute, then confirm that uncommitted files and native engine sessions resume.
  • Start the declared Compose environment and open an authenticated preview from another computer.
  • Trigger a configured GitHub agent and a scheduled agent, then confirm that each reuses its stable story.
  • Confirm that merge and archive retain the workspace and that explicit deletion removes only the chosen workspace.
  • Confirm that the GitHub App installation used for the test project has the intended maintainer permissions.
  • Confirm clean installation and 0.11 refusal behavior before approving the release.

Related to #272
Related to #273
Related to #274
Related to #275
Related to #276
Related to #277
Related to #278
Related to #279
Related to #280
Related to #281
Related to #282
Related to #283
Related to #284

@adrian-lorenzo adrian-lorenzo self-assigned this Sep 2, 2026
Make stories the durable unit of work, configure all agents through .agents, expose the workflow through MCP and the UI, and run Claude Code or Codex with full workspace and GitHub installation access.

BREAKING CHANGE: Facility 0.12 replaces the 0.11 run, receipt, approval, budget, delivery, and separate-preview model with persistent story workspaces and a new database schema.
@adrian-lorenzo
adrian-lorenzo force-pushed the feature/persistent-story-workspaces branch from 7eb262a to 181f1f3 Compare September 2, 2026 18:27
@adrian-lorenzo
adrian-lorenzo marked this pull request as draft September 2, 2026 18:49
@manuelgm0518

Copy link
Copy Markdown

Durable workspaces look directionally strong. Before this closes #284, could the PR include a criterion-by-criterion evidence table distinguishing automated CI, completed manual validation, and still-pending gates—especially the 14-day soak and live-engine pilots?

I’d also like to understand the threat model for giving GitHub-triggered reviewers and scheduled agents the same maintainer capability as builders when their inputs are explicitly untrusted.

@BrunoInz

BrunoInz commented Sep 2, 2026

Copy link
Copy Markdown

One thing I noticed while reading the 0.12 branch, in case it helps before this lands.

The workspace GitHub token is minted for the whole installation, not for the project's repositories.

  • services/api/src/github/client.tscreateGithubMaintainerTokenFactory calls POST /app/installations/{installation_id}/access_tokens with no repositories field (the comment says so explicitly: "No repository or permission override is sent"). On main the same call passes repositories: [repo].
  • services/api/src/github/workspace-credentials.ts — that token becomes GH_TOKEN / GITHUB_TOKEN in the turn environment. The credential helper (runner/facility-git-credential.mjs) only releases a token for a configured repo path, but that narrows git-over-HTTP only; gh is in the runner image and the turn prompt tells the agent it has GitHub maintainer access.
  • The test in services/api/test/agent-catalog-and-github-credentials.integration.test.ts asserts this behaviour by name ("issues one un-narrowed installation token to every configured repository").

Consequence: when two projects share an App installation, an agent in project A holds a maintainer-capable token for project B's repositories (and for any other repository the installation covers). That seems at odds with the boundary the epic keeps: #279 says "a request for another tenant, project, installation, or repository is denied", and the new security.md in this PR says "short-lived GitHub credentials scoped to configured project repositories".

Suggested fix: pass the project's repositories for that installation in the token request (repositories: [...]), grouped per installation as the broker already does. It stays uniform across agents (no per-agent permission calculation, which the epic bans) and only narrows to the project boundary. I'd add a case to the existing integration test asserting the repositories argument the factory receives.

Happy to open a PR for it if you'd take one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants