From 34e8d23e64f8447cf6e8474dc79db595b901503a Mon Sep 17 00:00:00 2001 From: Andres Contreras Date: Tue, 13 Oct 2026 20:55:19 -0700 Subject: [PATCH 1/3] Clarify current learning and tool execution boundaries --- docs/agent-integrations.md | 2 +- docs/assistant-files.md | 2 +- docs/brand.md | 6 +++--- docs/bridge.md | 4 ++-- docs/design-review.md | 6 +++--- docs/learning.md | 6 +++--- docs/release-notes.md | 4 ++-- docs/repository-workflow.md | 26 +++++++++++++------------- docs/scm.md | 32 ++++++++++++++++---------------- docs/search-providers.md | 4 ++-- docs/tool-broker.md | 6 +++--- 11 files changed, 49 insertions(+), 49 deletions(-) diff --git a/docs/agent-integrations.md b/docs/agent-integrations.md index 5321afe3..1205f081 100644 --- a/docs/agent-integrations.md +++ b/docs/agent-integrations.md @@ -236,4 +236,4 @@ The additional MCP tools retain the same API contracts: | Mediated search | `get_internet_search_configuration`, `configure_internet_search`, `test_internet_search`, `search_internet`, `get_search_citation` | | Recorded browser image | `get_browser_evidence_image` | -`configure_internet_search` takes a private key-file path on the **MCP host**, rather than a model-visible key, or an explicit `clear_secret`. The factory server receives the key through its authenticated write-only configuration API. `get_browser_evidence_image` returns native MCP image content from an existing authenticated capture. The Forge transport remains stdio; these tools do not create a public HTTP MCP connector. +`configure_internet_search` takes a private key-file path on the **MCP host**, rather than a model-visible key, or an explicit `clear_secret`. The factory server receives the key through its authenticated write-only configuration API. `get_browser_evidence_image` returns native MCP image content from an existing authenticated capture. The forge transport remains stdio; these tools do not create a public HTTP MCP connector. diff --git a/docs/assistant-files.md b/docs/assistant-files.md index 954a7984..af33f9d9 100644 --- a/docs/assistant-files.md +++ b/docs/assistant-files.md @@ -40,7 +40,7 @@ Image bytes are passed as multimodal content to the model. Their receipts record If an upload loses its reply, refresh the saved-file list before retrying. An exact retry uses the original request ID, filename and content and returns the same receipt. Changing the filename or bytes under that request ID is rejected. The UI keeps the pending file available for an explicit retry. -For a model response with an uncertain outcome, inspect the retained turn and its journal before sending again. Reusing the request ID requires the same message and file selection. Forge does not automatically repeat an uncertain model request. +For a model response with an uncertain outcome, inspect the retained turn and its journal before sending again. Reusing the request ID requires the same message and file selection. forge does not automatically repeat an uncertain model request. Each active turn has a persisted owner, an ownership epoch and a renewable lease measured against PostgreSQL's clock. Reading it from another API instance leaves the active request alone. If the lease expires, an authorized editor can explicitly recover that epoch for reconciliation. Recovery fences late replies and records the observed expiry; it does not repeat the model call. See [Conversation recovery](recovery.md#recover-a-product-conversation). diff --git a/docs/brand.md b/docs/brand.md index 74f30ef3..d5b8dd92 100644 --- a/docs/brand.md +++ b/docs/brand.md @@ -7,7 +7,7 @@ description: Use firefly forge logos, Manrope typography and licensed brand asse # Use firefly forge visual assets The product name is **firefly forge**, in lowercase. Its identity combines the -unmodified Firefly parent artwork, a measured hairline separator and a Forge +unmodified Firefly parent artwork, a measured hairline separator and a forge wordmark with an original lowercase **f** emblem. The factory scene comes from the actual Firefly website geometry, rather than a separate flowchart style. @@ -60,7 +60,7 @@ SVG box to the shared shell grid, not a hand-typed product title. onboarding composition: projected machines, module platforms, connected conveyors, side lane, and separate API/event backbone pipes. Only material colors change between themes. Website business-step numbers and decision badges -are omitted so they do not imply a Forge runtime operation. +are omitted so they do not imply a forge runtime operation. The authoritative renderer is `src/scripts/factory-drawing.ts` in `firefly-oss/firefly-software-website`, pinned to @@ -126,7 +126,7 @@ than flattening to a single color. Communicate status in text as well as color. revisions and SHA-256 values for generated assets and application copies. Keep `NOTICE` with the marks, `OFL.txt` with fonts and the icon notices with their bundles. The separate Agent Bridge uses its own connected-span -identity; its assets are not part of the public Forge inventory. +identity; its assets are not part of the public forge inventory. ## Regenerate and review diff --git a/docs/bridge.md b/docs/bridge.md index 098196ab..ca500b28 100644 --- a/docs/bridge.md +++ b/docs/bridge.md @@ -7,7 +7,7 @@ description: Connect forge to a local Firefly Agent Bridge for development testi Firefly Agent Bridge runs on the machine with your official Codex or Claude Code subscription login. Get its source and installation instructions from the [Firefly Agent Bridge repository](https://github.com/fireflyframework/firefly-agent-bridge). -Install and operate it separately from Forge, then issue a scoped bridge token for the factory's application connection. Bridge execution is intended for local development and testing; provider account terms and subscription limits still apply. +Install and operate it separately from forge, then issue a scoped bridge token for the factory's application connection. Bridge execution is intended for local development and testing; provider account terms and subscription limits still apply. forge includes a client connection to an OpenAI-compatible bridge endpoint. The bridge runs separately and authenticates its own clients. Configure the @@ -32,7 +32,7 @@ The catalog distinguishes configured access from a successful connection test. T endpoint rejects a request, inspect the run error and ask the bridge operator to verify the authorized configuration. -Forge also supports direct [Codex, Claude Code and native Firefly Agentic](agents.md) connections. Choose the provider and execution boundary that meet the project's requirements. +forge also supports direct [Codex, Claude Code and native Firefly Agentic](agents.md) connections. Choose the provider and execution boundary that meet the project's requirements. ## Verify image input for the selected configuration diff --git a/docs/design-review.md b/docs/design-review.md index 342475ca..9b8c7128 100644 --- a/docs/design-review.md +++ b/docs/design-review.md @@ -4,7 +4,7 @@ description: Review wireframes by customer journey and retain approved design re # Review customer journey wireframes -Generate references before implementation so customers and developers can check each journey, its acceptance criteria, and the shared layout. Forge renders a deterministic wireframe from the current product definition and project brand kit. The result is a review template, not an AI-generated finished product or proof of final visual acceptance. +Generate references before implementation so customers and developers can check each journey, its acceptance criteria, and the shared layout. forge renders a deterministic wireframe from the current product definition and project brand kit. The result is a review template, not an AI-generated finished product or proof of final visual acceptance. The product wizard shows every journey at desktop and mobile sizes in light and dark themes. Review all four frames for every screen before acknowledging the bundle. If the customer definition, reviewed architecture, or project brand changes, generate and review a new bundle. @@ -46,7 +46,7 @@ Add this metadata to the unchanged definition, using the actual returned digest } ``` -This is an excerpt to merge into the full product definition, not a standalone product save request. Save through `PUT /api/v1/repositories/{repository_id}/product`. Forge checks that the immutable saved bundle belongs to that repository, matches the definition and current brand digest, and includes all screens, viewports, and themes. Invented digests, missing references, duplicate screen attestations, and stale reviews are rejected. +This is an excerpt to merge into the full product definition, not a standalone product save request. Save through `PUT /api/v1/repositories/{repository_id}/product`. forge checks that the immutable saved bundle belongs to that repository, matches the definition and current brand digest, and includes all screens, viewports, and themes. Invented digests, missing references, duplicate screen attestations, and stale reviews are rejected. Existing brief-only workflows can omit `design_review`. The product wizard requires the reference acknowledgement before its final product save. Acknowledgement records the operator's review; it does not replace accessibility testing or a customer's acceptance of the implemented application. @@ -64,7 +64,7 @@ Inspect the actual application separately at wide and narrow widths, both themes The product definition carries a typed, editable `visual_design` contract. Each product saves its own settings; references freeze those values together with the product's brand digest. Logical project components may inherit their project's canonical product definition. Factory interface settings do not change customer products. -Defaults use a 4 px spacing baseline, 12 desktop columns with 24 px gutters and 32 px outer margins, and four mobile columns with 16 px gutters and 20 px margins. The desktop shell defaults to a 76 px header, 248 px rail and 64 px footer. Reference canvases default to 1440 × 900 and 390 × 844 px. The preview is a review template for the customer product; it does not reproduce the Forge portal or claim to be a finished interface. +Defaults use a 4 px spacing baseline, 12 desktop columns with 24 px gutters and 32 px outer margins, and four mobile columns with 16 px gutters and 20 px margins. The desktop shell defaults to a 76 px header, 248 px rail and 64 px footer. Reference canvases default to 1440 × 900 and 390 × 844 px. The preview is a review template for the customer product; it does not reproduce the forge portal or claim to be a finished interface. | Product setting | Supported values | | --- | --- | diff --git a/docs/learning.md b/docs/learning.md index 9b507b2c..6b73fe4e 100644 --- a/docs/learning.md +++ b/docs/learning.md @@ -5,7 +5,7 @@ description: Understand scoped learning, knowledge-first lookup, failed-attempt # Learn from evidence and reuse knowledge safely -A useful lesson explains which question it addresses, what was observed and where that observation still applies. Forge already retrieves required flycanon context and retains verified repository memory. The learning subsystem adds deterministic eligibility, exact question-to-evidence associations and controls for repeating an unchanged repair attempt. +A useful lesson explains which question it addresses, what was observed and where that observation still applies. forge already retrieves required flycanon context and retains verified repository memory. The initial learning implementation adds deterministic eligibility and exact question-to-evidence associations. Repeat-control rules remain a pure contract awaiting runtime integration. !!! info "Implementation boundary" The initial learning slice implements scoped PostgreSQL records, evidence eligibility, native knowledge-first decisions and explicit flycanon projection. Local qualification includes an actual indexed source, citation and local embedding, with repeat submission creating no duplicate. Automatic retrieval from projected learning namespaces, classified negative repair circuits, a complete Studio learning workflow and sharing between projects remain open. Generic failed checks record an observed outcome; they do not prove that an approach fails everywhere. @@ -24,7 +24,7 @@ Learning policy applies to new runs; admitted runs retain their captured policy. } ``` -The values shown are defaults. Disabling the policy affects later admissions. Required framework knowledge remains a separate setting and can still block generation when unavailable. +The values shown are defaults. The repeated-attempt setting belongs to the pure repair-control contract and is not enforced by the worker repair loop. Existing repair and clarification budgets still govern execution. Disabling the policy affects later admissions. Required framework knowledge remains a separate setting and can still block generation when unavailable. | Action | CLI command | Factory MCP tool | | --- | --- | --- | @@ -103,7 +103,7 @@ A negative record says that an observed command or check failed in an exact cont ## Bound repairs while preserving independent QA -An attempt fingerprint identifies the repository/component, exact source tree, dependency context, command and bounded outcome evidence. Repeat control applies to repair generation. If the worker proposes the same failed attempt without changing its context, the orchestrator can hold that attempt and request a different approach or an authorized override. +An attempt fingerprint identifies the repository/component, exact source tree, dependency context, command and bounded outcome evidence. The pure repeat-control rules apply to repair-generation proposals and preserve independent verification. They are not yet wired into the orchestrator's repair loop. Holding an unchanged failed attempt or requesting an authorized override still requires that integration and its qualification. Independent final verification still runs. Diagnostics, intentional RED tests and regression checks retain an explicit purpose and cannot be skipped because a previous failure exists. Existing repair and clarification budgets continue to limit execution. A known failure is useful input for the next implementation decision; it cannot substitute for current QA. diff --git a/docs/release-notes.md b/docs/release-notes.md index c69c63b9..f7f96f03 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -88,7 +88,7 @@ profiles. Angular, React, Next.js and Vue are selectable frontend contracts. the provider and preserves each attempt's results. - Final passing verification tied to a source-tree snapshot, checked again before publication, plus exclusive service ownership of each data directory. -- Token protection across the Forge API, with native management/admin/actuator +- Token protection across the forge API, with native management/admin/actuator surfaces disabled in the packaged pyfly configuration. - Firefly branding, self-hosted typography, accessible SVG diagrams and task-based installation, product, integration and operations guides. @@ -99,7 +99,7 @@ The configured factory gate covers Python tests, lint, formatting and types; Studio types, lint, formatting, unit tests, browser journeys and production build; and strict MkDocs compilation. -Studio was also exercised against the real authenticated Forge API: connection, +Studio was also exercised against the real authenticated forge API: connection, repository and run creation, cancellation, run diff and verification evidence, an actual ZIP download and the product form. Direct browser review covered 1440, 640, 390 and 360 pixel widths without diff --git a/docs/repository-workflow.md b/docs/repository-workflow.md index 348081a2..4d64161f 100644 --- a/docs/repository-workflow.md +++ b/docs/repository-workflow.md @@ -4,7 +4,7 @@ description: Use repository conversations to define products, request changes an # Work from your repository -Forge accepts trusted commands from GitHub issue and pull request comments, GitLab issue and merge request notes, and Bitbucket Cloud pull request comments. A command queues the same verified factory run used by Studio. Each run has its own workspace and branch, and retains the initiating receipt and issue identity. +forge accepts trusted commands from GitHub issue and pull request comments, GitLab issue and merge request notes, and Bitbucket Cloud pull request comments. A command queues the same verified factory run used by Studio. Each run has its own workspace and branch, and retains the initiating receipt and issue identity. | Provider | Inbound verification | Supported conversations | | --- | --- | --- | @@ -12,14 +12,14 @@ Forge accepts trusted commands from GitHub issue and pull request comments, GitL | GitLab | Constant-time comparison of `X-Gitlab-Token` | Issues and merge requests | | Bitbucket Cloud | HMAC-SHA256 over the original body; `X-Hub-Signature` | Pull requests | -Bitbucket Cloud [removed its native issue tracker API in August 2026](https://developer.atlassian.com/cloud/bitbucket/changelog/). Forge rejects Bitbucket issue creation and legacy issue events. Use a pull request conversation; a Jira connector is not part of this integration. +Bitbucket Cloud [removed its native issue tracker API in August 2026](https://developer.atlassian.com/cloud/bitbucket/changelog/). forge rejects Bitbucket issue creation and legacy issue events. Use a pull request conversation; a Jira connector is not part of this integration. ## Connect and authorize -1. Connect the repository through [your GitHub App or provider OAuth application](scm.md). Forge checks its current grants before remote writes. GitHub issue authoring needs the App's **Issues: write** permission; pull request comments also use **Pull requests: write**. GitLab needs its configured API grant. Bitbucket needs pull request write access. -2. Open repository collaboration settings. Enable the workflow and enter exact trusted actor IDs: numeric GitHub/GitLab user IDs or Bitbucket account UUIDs. Names, organization membership, labels, and a comment mentioning Forge do not grant execution rights. -3. Save a separate webhook secret. It is write-only; configuration responses show whether it is present. This secret is independent of the Forge operator token and provider OAuth/App credentials. -4. Configure the provider webhook to send comment events to `https://YOUR-FACTORY/api/v1/webhooks/PROVIDER/REPOSITORY_ID`. Use `github`, `gitlab`, or `bitbucket` and the Forge repository ID. Terminate HTTPS at your organization's gateway. The webhook route verifies provider credentials rather than accepting the operator token. +1. Connect the repository through [your GitHub App or provider OAuth application](scm.md). forge checks its current grants before remote writes. GitHub issue authoring needs the App's **Issues: write** permission; pull request comments also use **Pull requests: write**. GitLab needs its configured API grant. Bitbucket needs pull request write access. +2. Open repository collaboration settings. Enable the workflow and enter exact trusted actor IDs: numeric GitHub/GitLab user IDs or Bitbucket account UUIDs. Names, organization membership, labels, and a comment mentioning forge do not grant execution rights. +3. Save a separate webhook secret. It is write-only; configuration responses show whether it is present. This secret is independent of the forge operator token and provider OAuth/App credentials. +4. Configure the provider webhook to send comment events to `https://YOUR-FACTORY/api/v1/webhooks/PROVIDER/REPOSITORY_ID`. Use `github`, `gitlab`, or `bitbucket` and the forge repository ID. Terminate HTTPS at your organization's gateway. The webhook route verifies provider credentials rather than accepting the operator token. 5. Select the allowed commands, agent, product stack, publish setting, and operator-owned verification commands. Save a [product definition](product-definition.md) when the work needs a customer contract. GitHub and Bitbucket signatures are verified against raw bytes without JSON reserialization. GitLab's supported secret-token mode requires HTTPS; it does not provide a payload HMAC. GitLab's newer signing-token mode is not supported by this configuration. Consult [GitHub verification](https://docs.github.com/en/webhooks/using-webhooks/validating-webhook-deliveries), [GitLab webhook headers](https://docs.gitlab.com/user/project/integrations/webhooks/), and [Bitbucket webhook secrets](https://support.atlassian.com/bitbucket-cloud/docs/manage-webhooks/). @@ -36,7 +36,7 @@ Post one complete command as the comment. Options are a JSON object on the same /forge status {"run_id":"0123456789abcdef0123456789abcdef"} ``` -`run` and `maintain` require a brief. `test` runs the operator-selected verification plan and reports failures. New runs receive the current saved product revision; add `"product_revision":3` to require that exact current revision. If it changed before dispatch, Forge denies the command rather than silently using another contract. +`run` and `maintain` require a brief. `test` runs the operator-selected verification plan and reports failures. New runs receive the current saved product revision; add `"product_revision":3` to require that exact current revision. If it changed before dispatch, forge denies the command rather than silently using another contract. Comments cannot supply executable arguments, provider credentials, arbitrary branches, publish flags, or deployment scripts. Agent, stack, verification commands, and publication come from the repository's operator settings. A command from an untrusted actor, another remote repository, or a revoked setting receives a denied receipt and creates no run. @@ -50,13 +50,13 @@ The deployment manager still checks repository ownership and the verified source ## Receipts and feedback -Forge records a delivery digest, provider identity, actor, command, decision, and associated run. GitHub delivery IDs, GitLab retry-stable `webhook-id`/`Idempotency-Key` headers, and Bitbucket request UUIDs prevent duplicate delivery processing. The originating comment ID also prevents a second run when a provider sends a new delivery ID for the same comment. A delivery ID reused for different bytes is rejected. +forge records a delivery digest, provider identity, actor, command, decision, and associated run. GitHub delivery IDs, GitLab retry-stable `webhook-id`/`Idempotency-Key` headers, and Bitbucket request UUIDs prevent duplicate delivery processing. The originating comment ID also prevents a second run when a provider sends a new delivery ID for the same comment. A delivery ID reused for different bytes is rejected. The inbox and run creation share a PostgreSQL transaction. The lifecycle worker runs under the factory's execution lease, dispatches accepted receipts once, and posts queued and completion status back to the originating conversation. `GET /api/v1/collaboration/receipts?repository_id=ID` exposes receipt state to authenticated operators. -Remote comments and issue creation record a durable write intent before calling the provider. A timeout, interrupted write, or uncertain response produces `feedback_status: needs_reconciliation`. Forge does not repost it automatically. Check the provider conversation before taking a new action; do not treat an unknown outcome as a failed write. After checking, authenticated operators can call `POST /api/v1/collaboration/receipts/ID/reconcile` with `outcome: posted` or `not_posted` and a note. This records the observed result and sends no remote request. If no comment was posted, request a new explicit `/forge status` rather than replaying the uncertain write. +Remote comments and issue creation record a durable write intent before calling the provider. A timeout, interrupted write, or uncertain response produces `feedback_status: needs_reconciliation`. forge does not repost it automatically. Check the provider conversation before taking a new action; do not treat an unknown outcome as a failed write. After checking, authenticated operators can call `POST /api/v1/collaboration/receipts/ID/reconcile` with `outcome: posted` or `not_posted` and a note. This records the observed result and sends no remote request. If no comment was posted, request a new explicit `/forge status` rather than replaying the uncertain write. -Operators can author GitHub/GitLab issues through `POST /api/v1/repositories/ID/issues` with `title`, `body`, and an optional `idempotency_key`. Reuse that key when retrying the same request. Without a key, identical title/body content shares a digest key. Changing content with the same key is rejected. Provider App/OAuth credentials authorize the outbound call; the Forge operator token is never used as a provider credential. +Operators can author GitHub/GitLab issues through `POST /api/v1/repositories/ID/issues` with `title`, `body`, and an optional `idempotency_key`. Reuse that key when retrying the same request. Without a key, identical title/body content shares a digest key. Changing content with the same key is rejected. Provider App/OAuth credentials authorize the outbound call; the forge operator token is never used as a provider credential. These flows are tested with isolated PostgreSQL schemas and provider HTTP transports. They do not claim a live customer repository has granted access or accepted a webhook. Validate those grants and one signed delivery on your deployment before enabling automatic work. @@ -73,7 +73,7 @@ permission acceptance. Configure the connected GitHub App or OAuth application once, then save operator-authenticated `PUT /api/v1/collaboration/bootstrap/PROVIDER`. The setup fields are `enabled`, `auto_register`, `trusted_actors`, `configuration_actors`, `defaults`, and the write-only `webhook_secret`. `configuration_actors` must be a subset of trusted actors. Defaults use the repository collaboration schema; verification commands, stack, agent, publication and deployment settings remain operator-owned. -Point the provider webhook at `https://YOUR-FACTORY/api/v1/webhooks/PROVIDER`. No Forge repository ID is needed in this route. The first trusted structured comment can register its repository only after a fresh application-grant check confirms its exact remote identity and URL. Existing repository settings are preserved. GitHub installation and added-repository events must match the configured App installation; registration uses fresh granted repositories, not a repository list asserted by the payload. [GitHub documents these installation events](https://docs.github.com/en/webhooks/webhook-events-and-payloads). +Point the provider webhook at `https://YOUR-FACTORY/api/v1/webhooks/PROVIDER`. No forge repository ID is needed in this route. The first trusted structured comment can register its repository only after a fresh application-grant check confirms its exact remote identity and URL. Existing repository settings are preserved. GitHub installation and added-repository events must match the configured App installation; registration uses fresh granted repositories, not a repository list asserted by the payload. [GitHub documents these installation events](https://docs.github.com/en/webhooks/webhook-events-and-payloads). `GET /api/v1/collaboration/bootstrap/PROVIDER` returns readiness booleans and a relative ingress path. It never returns a webhook secret or invents a publicly reachable URL. Operators provide their deployment's HTTPS address. `GET /api/v1/collaboration/bootstrap/PROVIDER/receipts` returns the latest global receipts, including denied events and the routed repository receipt ID. Dispatch rechecks global actor permissions and current repository grants, so removal after delivery prevents queued work. @@ -122,11 +122,11 @@ An issue-bound paused run can post its durable worker request through the same f Project discovery returns at most 20 authorized registered repositories and a `next_cursor`. Supply that cursor in the next `project` request with `operation: repositories`. A repository appears only when the connected provider grants its exact remote ID, canonical name and URL, and the signed actor has configuration permission and the `project` command in that repository. The list does not reveal other registered projects. -A project configuration includes the native `ProjectDefinition` fields: name, description, topology, owners, components, and optional canonical `product_repository_id`, dependencies and roles. Component verification commands must remain omitted or empty in the comment. Forge copies each component repository's operator-owned checks. Comments cannot replace shell arguments, webhook secrets, model credentials or the global role registry. +A project configuration includes the native `ProjectDefinition` fields: name, description, topology, owners, components, and optional canonical `product_repository_id`, dependencies and roles. Component verification commands must remain omitted or empty in the comment. forge copies each component repository's operator-owned checks. Comments cannot replace shell arguments, webhook secrets, model credentials or the global role registry. Every current project member must use the same connected provider as the signed origin, grant the exact actor and enable the requested action. Mutations, source inspection starts/reviews, repository discovery and model turns also require configuration permission across the graph. Reading older source reports or conversations checks their frozen member graph too. Removing a repository or revoking its grant does not authorize disclosure of its old evidence. A numeric actor ID from GitHub cannot stand in for a GitLab or Bitbucket identity. -Start a conversation with `/forge assistant {"operation":"start","project_id":"PROJECT_ID"}`. The operator's `assistant_agent` setting selects `bridge` or `agentic`; comment authors cannot override it. Forge uses the project's canonical product repository after authorizing all members. Starting freezes configuration without invoking a model. A `turn` invokes the configured model and records its response under a caller-supplied request ID. Reusing the same ID and message retrieves the known response. A different message cannot reuse that ID. The reply remains unreviewed and cannot create a run, change the saved product, answer a worker, resume execution, publish code or deploy. +Start a conversation with `/forge assistant {"operation":"start","project_id":"PROJECT_ID"}`. The operator's `assistant_agent` setting selects `bridge` or `agentic`; comment authors cannot override it. forge uses the project's canonical product repository after authorizing all members. Starting freezes configuration without invoking a model. A `turn` invokes the configured model and records its response under a caller-supplied request ID. Reusing the same ID and message retrieves the known response. A different message cannot reuse that ID. The reply remains unreviewed and cannot create a run, change the saved product, answer a worker, resume execution, publish code or deploy. Use `analysis start` to inspect the authorized source components. `analysis review` acknowledges the exact observed-source digest, not functional behavior or a product design. Role configuration changes validated future-run defaults and preserves old run snapshots. Global role instructions and provider settings remain operator-only. diff --git a/docs/scm.md b/docs/scm.md index 41279678..6e2b5cef 100644 --- a/docs/scm.md +++ b/docs/scm.md @@ -7,13 +7,13 @@ description: Connect GitHub, GitLab or Bitbucket applications and retain project Open **Connections → Source control** to register an application owned by your GitHub organization, GitLab account or Bitbucket workspace. The provider controls -its permissions and grants. Forge discovers the repositories it can access and +its permissions and grants. forge discovers the repositories it can access and checks the current grant again before cloning or publishing. Application registration and owner approval happen on the provider's website. -Saving a client ID or private key in Forge does not create an application or prove +Saving a client ID or private key in forge does not create an application or prove access. **Test connection** makes a real provider request. **Connect repository** -accepts a discovered repository ID; Forge obtains its URL and default branch +accepts a discovered repository ID; forge obtains its URL and default branch from a fresh provider response. ## GitHub App @@ -22,23 +22,23 @@ from a fresh provider response. under the account that should own it. Request repository **Contents: read and write** and **Pull requests: read and write**. Install it on the selected repositories. Save its app ID, installation ID and unencrypted RSA private key -in Forge. The optional app slug provides a direct installation link. +in forge. The optional app slug provides a direct installation link. -Forge signs a short-lived app JWT. Repository discovery uses an installation +forge signs a short-lived app JWT. Repository discovery uses an installation token with metadata permission. Git execution and pull requests use a token restricted to the selected repository ID, with the two write permissions above. Tokens are refreshed before expiry. Organization approval, branch protections and workflow-file permissions can still prevent an operation. See [GitHub's installation-token contract](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-an-installation-access-token-for-a-github-app). -For GitHub Enterprise Server, set **Host** to its HTTPS origin. Forge derives +For GitHub Enterprise Server, set **Host** to its HTTPS origin. forge derives `/api/v3` from that origin and rejects clone URLs or pagination links from another host. App installation and permissions must exist on that same server. ## GitLab OAuth application [Register an OAuth application](https://docs.gitlab.com/integration/oauth_provider/) -with the redirect URI displayed in Forge: +with the redirect URI displayed in forge: ```text https://your-forge.example/api/v1/scm/connections/gitlab/callback @@ -49,7 +49,7 @@ Git access. Save the application ID and redirect URI; save its client secret when the application is confidential. Select **Authorize** to complete the provider's consent screen. -Forge uses authorization code with PKCE, a browser-bound state that expires +forge uses authorization code with PKCE, a browser-bound state that expires after ten minutes, and server-side rotating refresh tokens. State can be used once. Discovery requests non-archived projects where the authorized user is a member with developer access or higher. GitLab OAuth follows the user's grants; @@ -62,10 +62,10 @@ Self-managed GitLab uses its configured HTTPS origin. See the Create an [OAuth consumer in the workspace](https://support.atlassian.com/bitbucket-cloud/docs/use-oauth-on-bitbucket-cloud/) and enable `repository`, `repository:write`, `pullrequest` and `pullrequest:write`. Save its client key, client secret and workspace slug. The default grant is -**client credentials**: Forge obtains a short-lived token representing the +**client credentials**: forge obtains a short-lived token representing the consumer's owner and lists repositories in the configured workspace. -For user consent, choose **authorization code**, register the Forge callback +For user consent, choose **authorization code**, register the forge callback URI and select **Authorize**. This uses browser-bound, single-use state and rotating refresh tokens. Client-credentials tokens are obtained again on expiry; that grant does not require a refresh token. @@ -84,11 +84,11 @@ secret values cannot be read back. Blank secret fields preserve saved values. Use the explicit clear action to remove them. Changing application settings invalidates saved grants, cached tokens and pending authorization state. Changing an OAuth host or client ID also requires an explicitly supplied client -secret or an explicit clear action. Forge refuses to carry a confidential +secret or an explicit clear action. forge refuses to carry a confidential client's saved secret to that new destination. A public GitLab client can clear the old secret before starting a new authorization. -Before refreshing a rotating token, Forge persists a dispatch marker in its +Before refreshing a rotating token, forge persists a dispatch marker in its current private SCM grant storage. A timeout, invalid response or interrupted process leaves that marker unresolved; subsequent requests cannot send the old refresh token again, including after restart. The connection reports @@ -109,8 +109,8 @@ products. Coding and verification workers do not receive those credentials. Provider errors omit response bodies that might contain sensitive data. Revoke an installation or OAuth grant at the provider to remove its authority. -Forge's next grant check fails if access is revoked. Clearing the local secret -removes Forge's copy; it does not uninstall the provider's application. Existing +forge's next grant check fails if access is revoked. Clearing the local secret +removes forge's copy; it does not uninstall the provider's application. Existing remote branches and pull requests remain. ## Connection API @@ -142,7 +142,7 @@ or credentials supplied by the caller. Studio runs begin with `publish: false`. After verification succeeds, **Publish pull request** creates a branch and invokes the provider's pull-request API. Operators can also use `forge publish RUN_ID` or explicitly request `publish: -true` when creating a run. Forge does not merge a pull request or deploy a +true` when creating a run. forge does not merge a pull request or deploy a product through this action. Project reviews and branch protections still apply. Generic Git, Azure DevOps and Gitea retain the manual repository path. HTTPS URLs @@ -152,7 +152,7 @@ remain available; GitHub, GitLab and Bitbucket application connections provide the managed grant flow described above. Contract tests check request construction, state handling and failure boundaries. -Loopback HTTP acceptance runs a real Forge API server and a provider fixture +Loopback HTTP acceptance runs a real forge API server and a provider fixture server for GitHub Enterprise `/api/v3`, self-managed GitLab `/api/v4`, and Bitbucket Cloud. It verifies operator authentication, GitHub JWT signatures, GitLab PKCE and refresh rotation, bearer-authorized repository discovery, signed diff --git a/docs/search-providers.md b/docs/search-providers.md index 2c2ca263..0a39115b 100644 --- a/docs/search-providers.md +++ b/docs/search-providers.md @@ -182,8 +182,8 @@ The same fields are available through `PUT /api/v1/search/configuration`: `provi The Python SDK exposes `ForgeClient.configure_search(provider="tavily", api_key=authorized_key, include_domains=["github.com"])`. `search_configuration()`, `test_search()`, `internet_search()` and `search_citation()` use the same authenticated routes as the CLI. -Forge's own MCP server exposes `configure_internet_search(provider="tavily", api_key_file="/private/secrets/tavily.key", include_domains=["github.com"])`. The key file belongs to the MCP host. Its schema accepts the file path rather than a raw key. `search_internet`, `test_internet_search` and `get_search_citation` delegate to the factory API. MCP access does not bypass factory authorization. +forge's own MCP server exposes `configure_internet_search(provider="tavily", api_key_file="/private/secrets/tavily.key", include_domains=["github.com"])`. The key file belongs to the MCP host. Its schema accepts the file path rather than a raw key. `search_internet`, `test_internet_search` and `get_search_citation` delegate to the factory API. MCP access does not bypass factory authorization. -Search calls during implementation currently sit inside the implementation's execution intent. Separate intent and result receipts for each search invocation, provider-usage reconciliation after a process crash, and external MCP server connections are not implemented. See [capability limits](capabilities.md) for the factory's other execution boundaries. +Agent search invocations retain a committed intent before dispatch and a bounded result, rejection or unknown outcome afterward. The run-scoped broker enforces frozen budgets and current authority; an uncertain result requires explicit recovery without replaying the request. Automatic provider-usage reconciliation after a process crash is not implemented. Standalone administrator queries and connection tests use their separate API routes. Read [tool invocation evidence](tool-broker.md) for those receipts and recovery rules. [External MCP setup](external-tools.md) covers the reviewed GitHub repository-read adapter and the other templates' unmet prerequisites. See [capability limits](capabilities.md) for the factory's other execution boundaries. Provider contracts: [Tavily Search API](https://docs.tavily.com/documentation/api-reference/endpoint/search), [Brave web search](https://api-dashboard.search.brave.com/api-reference/web/search/get). diff --git a/docs/tool-broker.md b/docs/tool-broker.md index 4f2c094b..216146bc 100644 --- a/docs/tool-broker.md +++ b/docs/tool-broker.md @@ -5,7 +5,7 @@ description: Inspect durable tool intents, bounded outcomes, frozen budgets and # Review mediated tool execution -Agent internet searches and approved external MCP calls pass through a run-scoped broker. Before sending a request, forge records its intent and reserves a call from the run's frozen budget in PostgreSQL. The same transaction appends the intent to the run's hash-chain journal. +Native knowledge lookups, agent internet searches and approved external MCP calls pass through a run-scoped broker. Before sending a request, forge records its intent and reserves a call from the run's frozen budget in PostgreSQL. The same transaction appends the intent to the run's hash-chain journal. An invocation retains its correlation ID, execution stage, repository and project association, active role grants, actual source commit and tree, provider revision, hostname restrictions and policy. A successful result includes its `invocation_id`. Citations and provider usage receipts remain part of the recorded result. @@ -69,6 +69,6 @@ An invocation that may still be running cannot be abandoned until its bounded le Factory policy routes require a factory administrator. Run activity follows the existing project read permission. Reconciliation follows the run's execution permission and publication guards. The authenticated decision actor is recorded with the reconciliation event. -The Python SDK provides `tool_policy()`, `configure_tool_policy()`, `tool_invocations()` and `abandon_tool_invocation()`. Forge's MCP server provides `get_tool_policy`, `configure_tool_policy`, `get_run_tool_invocations` and `abandon_tool_invocation` through those same API routes. +The Python SDK provides `tool_policy()`, `configure_tool_policy()`, `tool_invocations()` and `abandon_tool_invocation()`. forge's MCP server provides `get_tool_policy`, `configure_tool_policy`, `get_run_tool_invocations` and `abandon_tool_invocation` through those same API routes. -The broker currently mediates agent internet search and approved external MCP calls. Workspace operations, commands and browser checks retain their existing execution guards and evidence mechanisms; this does not move every native or official CLI tool into the broker. External MCP calls require the [approved registry and project grants](external-tools.md). See [agent tools](agent-tools.md), [search providers](search-providers.md) and [capability limits](capabilities.md). +The broker currently mediates native knowledge lookup, agent internet search and approved external MCP calls. Workspace operations, commands and browser checks retain their existing execution guards and evidence mechanisms; this does not move every native or official CLI tool into the broker. External MCP calls require the [approved registry and project grants](external-tools.md). See [agent tools](agent-tools.md), [search providers](search-providers.md) and [capability limits](capabilities.md). From 1e4594310fb7017b8f595fbeb0dfc331e73dea5a Mon Sep 17 00:00:00 2001 From: Andres Contreras Date: Tue, 13 Oct 2026 20:41:58 -0700 Subject: [PATCH 2/3] Exclude TypeScript compiler caches from distributions --- pyproject.toml | 2 +- scripts/check_distribution.py | 7 ++++- tests/test_distribution.py | 58 +++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 tests/test_distribution.py diff --git a/pyproject.toml b/pyproject.toml index 84f6f5d8..8a780346 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,7 +55,7 @@ path = "scripts/build_hook.py" "studio/dist" = "studio/dist" [tool.hatch.build] -exclude = ["**/node_modules/**", "**/.venv/**", "**/__pycache__/**", "**/.codex/**", "**/AGENTS.md", "**/CLAUDE.md", "**/.env", "**/test-results/**", "**/playwright-report/**", "**/.forge/**", "/dist/**", "/build/**"] +exclude = ["**/node_modules/**", "**/.venv/**", "**/__pycache__/**", "**/*.tsbuildinfo", "**/.codex/**", "**/AGENTS.md", "**/CLAUDE.md", "**/.env", "**/test-results/**", "**/playwright-report/**", "**/.forge/**", "/dist/**", "/build/**"] [tool.pytest.ini_options] testpaths = ["tests"] diff --git a/scripts/check_distribution.py b/scripts/check_distribution.py index 26534c8e..05b3e9f1 100644 --- a/scripts/check_distribution.py +++ b/scripts/check_distribution.py @@ -35,7 +35,12 @@ def inspect(directory: Path) -> None: names = source.getnames() for name in names: parts = Path(name).parts - if DENIED.intersection(parts) or "bridge" in parts or any(part == "plans" for part in parts): + if ( + DENIED.intersection(parts) + or "bridge" in parts + or any(part == "plans" for part in parts) + or name.endswith(".tsbuildinfo") + ): raise SystemExit(f"Release contains excluded content: {name}") if archive.suffix == ".whl" and not any(name.endswith("static/index.html") for name in names): raise SystemExit("Wheel does not include the built Studio") diff --git a/tests/test_distribution.py b/tests/test_distribution.py new file mode 100644 index 00000000..d53d05a2 --- /dev/null +++ b/tests/test_distribution.py @@ -0,0 +1,58 @@ +# Copyright 2026 Firefly Software Foundation. SPDX-License-Identifier: Apache-2.0 +"""Release inspection rejects generated compiler state in either native archive.""" + +import io +import subprocess +import sys +import tarfile +import zipfile +from pathlib import Path + +import pytest + + +def archives(directory: Path, target: str | None = None, cache: str = "") -> None: + with zipfile.ZipFile(directory / "firefly_forge-0.2.3-py3-none-any.whl", "w") as wheel: + wheel.writestr( + "firefly_forge-0.2.3.dist-info/METADATA", + "Metadata-Version: 2.4\nName: firefly-forge\nVersion: 0.2.3\n" + "Requires-Dist: flycanon-sdk @ https://github.com/firefly-operationOS/flycanon/releases/" + "download/v26.10.0/flycanon_sdk-26.10.0-py3-none-any.whl\n", + ) + wheel.writestr("firefly_forge/static/index.html", "Studio fixture") + if target == "wheel": + wheel.writestr(cache, "generated compiler bookkeeping") + with tarfile.open(directory / "firefly_forge-0.2.3.tar.gz", "w:gz") as source: + entries = [ + ("firefly_forge-0.2.3/src/firefly_forge/__init__.py", b""), + ("firefly_forge-0.2.3/studio/dist/index.html", b"Studio fixture"), + ] + if target == "sdist": + entries.append(("firefly_forge-0.2.3/" + cache, b"compiler bookkeeping")) + for name, content in entries: + info = tarfile.TarInfo(name) + info.size = len(content) + source.addfile(info, io.BytesIO(content)) + + +def inspect(directory: Path) -> subprocess.CompletedProcess[str]: + script = Path(__file__).resolve().parents[1] / "scripts" / "check_distribution.py" + return subprocess.run([sys.executable, str(script), str(directory)], capture_output=True, text=True) + + +@pytest.mark.parametrize("target", ["wheel", "sdist"]) +@pytest.mark.parametrize( + "cache", ["tsconfig.tsbuildinfo", "studio/tsconfig.tsbuildinfo", "studio/nested/app.tsbuildinfo"] +) +def test_distribution_rejects_generated_typescript_caches(tmp_path, target, cache): + archives(tmp_path, target, cache) + result = inspect(tmp_path) + assert result.returncode != 0 + assert "excluded content" in result.stderr and cache in result.stderr + + +def test_distribution_preserves_source_and_bundled_studio(tmp_path): + archives(tmp_path) + result = inspect(tmp_path) + assert result.returncode == 0, result.stderr + assert result.stdout.count("Verified release boundary:") == 2 From 372c0d324442d47e2e290e935a962757527362a6 Mon Sep 17 00:00:00 2001 From: Andres Contreras Date: Tue, 13 Oct 2026 21:37:33 -0700 Subject: [PATCH 3/3] Wait for painted Mermaid geometry before asserting navigation readiness --- scripts/docs-browser/mermaid.spec.mjs | 35 +++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/scripts/docs-browser/mermaid.spec.mjs b/scripts/docs-browser/mermaid.spec.mjs index a6b7f662..b73ab24d 100644 --- a/scripts/docs-browser/mermaid.spec.mjs +++ b/scripts/docs-browser/mermaid.spec.mjs @@ -50,9 +50,14 @@ async function assertDiagrams(page, count) { await expect .poll(async () => { diagrams = await renderedDiagrams(page); - return diagrams.length; + return { + count: diagrams.length, + painted: diagrams.every( + (diagram) => diagram.width > 100 && diagram.height > 30, + ), + }; }) - .toBe(count); + .toEqual({ count, painted: true }); for (const diagram of diagrams) { expect(diagram.width).toBeGreaterThan(100); expect(diagram.height).toBeGreaterThan(30); @@ -71,6 +76,32 @@ async function assertDiagrams(page, count) { return diagrams; } +test("Mermaid readiness waits for geometry after SVG insertion", async ({ + page, +}) => { + await optionalRepositoryBadges(page); + await page.goto("product-lifecycle/"); + await assertDiagrams(page, 1); + await page.evaluate(() => { + const original = window.SVGElement.prototype.getBoundingClientRect; + let pending = true; + window.unpaintedDiagramSamples = 0; + window.SVGElement.prototype.getBoundingClientRect = function () { + const box = original.call(this); + if (pending && this.localName === "svg") { + pending = false; + window.unpaintedDiagramSamples += 1; + return new window.DOMRect(box.x, box.y, 0, 0); + } + return box; + }; + }); + const diagrams = await assertDiagrams(page, 1); + expect(await page.evaluate(() => window.unpaintedDiagramSamples)).toBe(1); + expect(diagrams[0].width).toBeGreaterThan(100); + expect(diagrams[0].height).toBeGreaterThan(30); +}); + for (const entry of mermaidPages(join(root, "docs"))) { for (const theme of ["light", "dark"]) { for (const width of [1440, 390]) {