Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion .github/workflows/cicd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ jobs:
tests:
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
runs-on: ubuntu-latest
timeout-minutes: 15
timeout-minutes: 35
name: tests (integration)
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand All @@ -107,6 +107,9 @@ jobs:
with:
node-version: "24"
package-manager-cache: false
- name: Install Chromium for integration hydration regressions
timeout-minutes: 20
uses: ./.github/actions/install-chromium
- name: Install Node resolver test dependencies
run: |
npm ci --ignore-scripts --prefix tests/node/resolver-dependencies
Expand Down Expand Up @@ -353,6 +356,21 @@ jobs:
START=$(date +%s)
deno task coverage:ci:shard -- --shard=${{ matrix.shard }}/4 --coverage-dir=coverage-shard-${{ matrix.shard }}
echo "duration=$(($(date +%s) - START))s" >> "$GITHUB_OUTPUT"
- name: Include dependency history integration coverage
if: matrix.shard == 1
run: |
rm -rf coverage-history
deno task test:file --coverage=coverage-history \
tests/integration/semantic-unit-boundary/src/platform/adapters/fs/veryfront/dependency-metadata-history.test.ts \
tests/integration/semantic-unit-boundary/src/platform/adapters/veryfront-api-client/dependency-metadata-history.test.ts \
tests/integration/semantic-unit-boundary/src/transforms/esm/package-registry-metadata-history.test.ts
deno coverage coverage-history --include=src/ --include=cli/ --exclude=/tests/ --exclude=/__tests__/ --lcov > coverage-history.lcov
deno eval '
import { mergeLcovReports } from "./scripts/test/coverage-ci.ts";
const target = "coverage-shard-1/lcov.info";
const reports = await Promise.all([target, "coverage-history.lcov"].map(path => Deno.readTextFile(path)));
await Deno.writeTextFile(target, mergeLcovReports(reports));
'
- name: Upload unit coverage lcov
if: ${{ !cancelled() }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
Expand Down
45 changes: 45 additions & 0 deletions docs/architecture/15-runtime-adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,51 @@ untrusted project code. Configured storage failures do not downgrade to local hi
The provider, handle, record types, and handle factory are exported by
`veryfront/platform`.

## API-derived dependency metadata history

`FileSystemAdapter.readDependencyMetadataHistory()` is an optional read-only
capability, separate from `RuntimeAdapter.dependencySnapshotStore`. The Veryfront
filesystem implements it through the existing authenticated project API and the
same project-scoped token used for file reads. It does not obtain shared internal
credentials or expose a snapshot publication endpoint to project code.

Before a dependency-resolution write changes `package.json`, the API acknowledges
storage of its own observed prior dependency map, including an absent file as an
empty map. Publication atomically bounds unexpired history to 16 maps and 960 KiB
per project/branch. A full budget defers automatic writeback instead of dropping a
retained map. The read endpoint returns that history with a response limit of 1 MiB.
The project and canonical branch scope are derived by the API, with the requested
branch retained in the response for matching.

The optional reader accepts an `AbortSignal`. The registry's five-second deadline
aborts the underlying metadata request, so cooperative reads release their admission
slots when the endpoint stalls. Concurrent keys from one source share the same
full-history read, then independently validate their requested key. Settled history
is retained for one second, including misses, with limits of 32 sources and 8 MiB
of serialized metadata. A change to the observed package dependency state invalidates
the source cache immediately; registry clearing also discards it. Only copied,
validated fields are retained, and original snapshot expiries still apply. Returned
dependency maps have a null prototype.

Disabling pinning or reducing the rollout cohort stops new pinning. Exact historical
keys remain readable through their existing expiry, using the current captured
configuration and the same project/branch checks; recovery grants no writeback authority.

A cold renderer consults this capability only after local history misses and the
current dependency key differs. It combines a prior raw map with its current
captured React/Veryfront configuration and accepts it only if the exact requested
key matches. Scope mismatch, corrupt data, outages and expired records fail closed.
Recovered data keeps its acknowledged expiry and never becomes current writeback
authority. Reader methods are captured before use and remain associated with their
original source when file reads are wrapped for tracking.

This does not weaken the shared snapshot store's acknowledged publication contract:
an explicitly configured store never falls back to this metadata reader. Standalone
filesystems do not require an API or shared backend. Direct package edits, concurrent
configuration changes, expired history, and metadata changes predating the API
preimage publisher can remain unavailable. They still return a conflict rather
than interpreting an old key using current dependencies.

## Change checks

- Update [support matrix](./20-support-matrix.md) when runtime support changes.
Expand Down
37 changes: 19 additions & 18 deletions docs/architecture/20-support-matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,24 +32,25 @@ These are the runtime capability profiles modeled by the framework.
This matrix separates open-core framework support from capabilities that depend
on a backing API or cloud bootstrap.

| Capability | Current support shape | Notes |
| --------------------------------------------------------------------- | ------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| Routing, rendering, middleware, API routes | Open-core | Core framework capability. |
| Shared dependency snapshot history | Open-core with an explicit host provider | Requires native proxy detection on Deno, Node, or Bun. Hosts without it retain default process-local history. |
| App MCP server | Open-core on runtimes that can host it | Not available on constrained runtimes like Cloudflare Workers. |
| Internal AG-UI transport | Open-core runtime surface | Separate from the app MCP contract. |
| Direct provider integrations (`openai`, `anthropic`, `google`, local) | Open-core with provider credentials/runtime setup | Depends on the selected provider configuration. |
| Extension contracts (auth, bundler, CSS, parser, observability, etc.) | Open-core | First-party `@veryfront/ext-*` packages provide implementations. |
| Workflow engine (in-memory and Redis backends) | Open-core | In-memory, Redis, and process run execution work without Kubernetes. |
| Discovery (tools, agents, workflows, prompts, resources, skills) | Open-core | Convention-based file-system discovery at server startup. |
| Veryfront Cloud model routing | Requires Veryfront Cloud bootstrap | Depends on project/auth context and cloud gateway configuration. |
| Veryfront Cloud blob storage | Requires Veryfront Cloud bootstrap | Uses project-scoped cloud upload APIs. |
| Veryfront Cloud agent service | Requires Veryfront Cloud bootstrap | Hosted agent execution with project steering and runtime system messages. |
| Runs client | Requires backing API/service layer | Exposed as SDK/API surface for task, workflow, and agent execution. |
| Sandbox | Requires backing API/service layer | Depends on authenticated sandbox session APIs. |
| Remote integration tools | Requires backing API/service layer | Tool definitions and execution are fetched per request from the configured API layer. |
| Local catalog integration tools | Open-core with provider credentials/runtime setup | Exact-grant local sources execute supported HTTPS REST endpoints directly. Salesforce also has a dedicated service-account source. |
| Control-plane agent routing | Requires Veryfront Cloud bootstrap | EdDSA-signed request validation for hosted agent orchestration. |
| Capability | Current support shape | Notes |
| --------------------------------------------------------------------- | ---------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Routing, rendering, middleware, API routes | Open-core | Core framework capability. |
| Shared dependency snapshot history | Open-core with an explicit host provider | Requires native proxy detection on Deno, Node, or Bun. Hosts without it retain default process-local history. |
| Prior dependency metadata | Optional Veryfront project API filesystem capability | Read-only recovery for dependency-resolution writebacks; requires exact project/branch and dependency-key matching. Not general shared snapshot storage. |
| App MCP server | Open-core on runtimes that can host it | Not available on constrained runtimes like Cloudflare Workers. |
| Internal AG-UI transport | Open-core runtime surface | Separate from the app MCP contract. |
| Direct provider integrations (`openai`, `anthropic`, `google`, local) | Open-core with provider credentials/runtime setup | Depends on the selected provider configuration. |
| Extension contracts (auth, bundler, CSS, parser, observability, etc.) | Open-core | First-party `@veryfront/ext-*` packages provide implementations. |
| Workflow engine (in-memory and Redis backends) | Open-core | In-memory, Redis, and process run execution work without Kubernetes. |
| Discovery (tools, agents, workflows, prompts, resources, skills) | Open-core | Convention-based file-system discovery at server startup. |
| Veryfront Cloud model routing | Requires Veryfront Cloud bootstrap | Depends on project/auth context and cloud gateway configuration. |
| Veryfront Cloud blob storage | Requires Veryfront Cloud bootstrap | Uses project-scoped cloud upload APIs. |
| Veryfront Cloud agent service | Requires Veryfront Cloud bootstrap | Hosted agent execution with project steering and runtime system messages. |
| Runs client | Requires backing API/service layer | Exposed as SDK/API surface for task, workflow, and agent execution. |
| Sandbox | Requires backing API/service layer | Depends on authenticated sandbox session APIs. |
| Remote integration tools | Requires backing API/service layer | Tool definitions and execution are fetched per request from the configured API layer. |
| Local catalog integration tools | Open-core with provider credentials/runtime setup | Exact-grant local sources execute supported HTTPS REST endpoints directly. Salesforce also has a dedicated service-account source. |
| Control-plane agent routing | Requires Veryfront Cloud bootstrap | EdDSA-signed request validation for hosted agent orchestration. |

## Extension contract matrix

Expand Down
7 changes: 4 additions & 3 deletions src/modules/server/module-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -392,8 +392,10 @@ interface ModuleDependencyState {
/** Serve transformed module at /_vf_modules/* path */
export function serveModule(req: Request, options: ModuleServerOptions): Promise<Response> {
const url = new URL(req.url);
const pathPin = getHostEnv(DEPENDENCY_PINNING_ENV_FLAG) === "1"
? extractDependencyPinningPathKey(url.pathname)
const dependencyPinningEnabled = getHostEnv(DEPENDENCY_PINNING_ENV_FLAG) === "1";
const extractedPathPin = extractDependencyPinningPathKey(url.pathname);
const pathPin = dependencyPinningEnabled || extractedPathPin.found
? extractedPathPin
: { pathname: url.pathname, found: false, malformed: false };
if (pathPin.found && !pathPin.malformed) {
url.pathname = pathPin.pathname;
Expand Down Expand Up @@ -427,7 +429,6 @@ export function serveModule(req: Request, options: ModuleServerOptions): Promise
const requestedPinKey = pathPin.found ? pathPin.cacheKey : queryPinValues[0];
const requestedPinCount = queryPinValues.length + (pathPin.found ? 1 : 0);
const hasRequestedPinKey = requestedPinCount > 0;
const dependencyPinningEnabled = getHostEnv(DEPENDENCY_PINNING_ENV_FLAG) === "1";
if (
pathPin.malformed ||
requestedPinCount > 1 ||
Expand Down
4 changes: 4 additions & 0 deletions src/platform/adapters/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,10 @@ export interface FileSystemAdapter {
* callers must not carry freshness across a possible context change.
*/
getSourceSnapshotIdentity?(): string | undefined | Promise<string | undefined>;
/** Read trusted prior dependency metadata for this adapter's mutable source scope. */
readDependencyMetadataHistory?(signal?: AbortSignal): Promise<
import("./dependency-metadata-history.ts").DependencyMetadataHistory
>;
}

/** A filesystem adapter that advertises genuine bounded byte reads. */
Expand Down
11 changes: 11 additions & 0 deletions src/platform/adapters/dependency-metadata-history.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export interface DependencyMetadataHistoryEntry {
readonly dependencies: Readonly<Record<string, string>>;
readonly expiresAt: number;
}

export interface DependencyMetadataHistory {
readonly version: 1;
readonly projectId: string;
readonly branch: string | null;
readonly entries: readonly DependencyMetadataHistoryEntry[];
}
4 changes: 4 additions & 0 deletions src/platform/adapters/fs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,7 @@ export {
wrapFSAdapter,
} from "./wrapper.ts";
export type { ExtendedFileSystemAdapter } from "./wrapper.ts";
export type {
DependencyMetadataHistory,
DependencyMetadataHistoryEntry,
} from "../dependency-metadata-history.ts";
13 changes: 13 additions & 0 deletions src/platform/adapters/fs/veryfront/adapter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { logger as baseLogger } from "#veryfront/utils";
import { awaitAbortable, throwIfAborted } from "#veryfront/utils/abort.ts";
import { createHash, type Hash } from "node:crypto";
import { createError, toError } from "#veryfront/errors";
import type {
Expand All @@ -17,6 +18,7 @@ import type {
ResolveFileOptions,
SourceSnapshotFreshnessOptions,
} from "#veryfront/platform/adapters/base.ts";
import type { DependencyMetadataHistory } from "#veryfront/platform/adapters/dependency-metadata-history.ts";
import { VeryfrontApiClient } from "../../veryfront-api-client/index.ts";
import type { Project } from "../../veryfront-api-client/index.ts";
import { FileCache } from "../cache/file-cache.ts";
Expand Down Expand Up @@ -1640,6 +1642,17 @@ export class VeryfrontFSAdapter implements FSAdapter {
return this.#getCurrentSourceSnapshotIdentity();
}

async readDependencyMetadataHistory(signal?: AbortSignal): Promise<DependencyMetadataHistory> {
throwIfAborted(signal);
await awaitAbortable(this.#ensureExactReadInitialized(), signal);
const source = this.getEffectiveContentContext();
if (source?.sourceType !== "branch") {
throw new TypeError("Dependency metadata history is available only for branch sources");
}
const branch = source.branch && source.branch !== "main" ? source.branch : null;
return await this.client.readDependencyMetadataHistory(branch, signal);
}

getPokeMetrics(): {
received: number;
invalidationsTriggered: number;
Expand Down
Loading
Loading