Repository navigation
0.5.0 (2026-10-06)
forgec build now emits ForgeGraph's contract IR (contract.json), and
forgec import-contract and forgec contract-diff take it back in and compare
it. Projections' latest aggregate is breaking: it is now
latest <field> by <orderField>. References accept "$self", floor and
datetime arithmetic evaluate at runtime, and the console gains Forgejo Git
projects and an Integrations tab backed by ForgeGraph's contract registry.
Added
-
Console Forgejo Git projects: a project may set
"provider": "forgejo"and
commit throughFORGEJO_URLandFORGEJO_TOKEN(minted by
forge forgejo-token) instead of a GitHub token./healthzreports a
configured provider's missing credential, and a Forgejo-only instance no
longer needsGITHUB_TOKEN. -
Console integrations: with
FORGEGRAPH_URLand a read-scopeFORGEGRAPH_TOKEN, every
ForgeGraph app is listed as an integration described by its published contract (contract IR v1,
converted to OpenAPI 3.1), and its operations can be called through the console. Base URLs,
credentials (bearer, header or Cloudflare Access service token,INTEGRATION_*secrets only) and
write access are set per app inINTEGRATIONS_JSON; reads are the default, writes need an opt-in
and a per-call confirmation and are audited. The playground's new Integrations tab browses
operations by group, builds requests from their schemas, confirms writes, keeps drafts per
operation, and can hand an operation to the Graph tab's OpenAPI importer. -
forgec buildwritescontract.json, ForgeGraph's contract IR v1, next to
openapi.jsonand derived from it: one operation per@crudroute,@http
function and workflow route, ids<serviceId>.<group>.<endpoint>
(--service-idsets the app slug; the default is the unscoped package name),
authentication from the security requirement, and each operation's SLO as
policy.slo. Fingerprints are hashed over the bytesJSON.stringifywould
produce, so ForgeGraph'svalidateContractaccepts the file as written; the
conformance suite runs that validator, vendored, over a curated set of contracts
and CI sweeps every package in the repository withscripts/validate-contracts.sh.
fg contract publish --file generated/contract.jsonpublishes it. -
forgec import-contract <contract.json> --package <name> --out <dir>turns a
contract IR document into a Forge package through OpenAPI 3.1 and the OpenAPI
importer, reporting RPC procedures, unbindable methods, anonymous or public
operations, SLA/SLO policy, middleware, alternate media and non-Problem error
bodies instead of approximating them. A contract whose fingerprints do not
match is refused. -
forgec contract-diff <a> <b> [--ignore <kind>]compares two contracts per
route (request and response schemas after$refinlining, statuses, policy)
and exits 1 on any difference not declared expected. Seedocs/contract-ir.md
for the round trip and which differences it is expected to show. -
@forgegraph/foundation/configuration(experimental): typed parameterization
against a pinned specification revision, layered defaults and overrides,
deterministic resolution, and resolutions identified by a digest over their
values.Configurationin@forgegraph/runtimeresolves a chain and records it.Configuration is not arbitrary JSON: every value assigns a parameter the pinned
revision declared, so an unknown key cannot be set and a key retyped in a later
revision cannot be silently inherited. Layers carry an ordinal and a child's is
strictly greater than its parent's, so precedence is a total order and resolution
gives the same answer whichever end of the chain you start from. Every resolved
value records which configuration supplied it, so a surprising value is
attributable without re-deriving the chain by hand.Secret parameters carry a reference and never a literal, may not carry a default,
and resolve to that reference. This package cannot dereference a binding, and a
test scans the compiled contract for any field that could hold secret material.Seven criteria have local evidence; scalar type validation remains tracked in #94.
-
@forgegraph/foundation/reachability(experimental): contact points and service
endpoints as durable facts, with purpose labels, preference ranking, half-open
validity, verification evidence and explicit supersession.Reachabilityin
@forgegraph/runtimedeclares, verifies, supersedes and resolves them.Contact points and endpoints are separate resources rather than one polymorphic
locator, because a data classification is a property of a field. A singlevalue
column has to be classified once: asdata.contact, which sweeps webhook URLs into
subject-rights erasure that does not apply to them, or as structural, which
under-classifies an email address.LocatorKind.personaldecides which profile a
kind belongs to and a rule on each profile enforces it.No transport mechanics are foundation semantics here: a locator says where something
can be reached, never whether a message was sent, how many attempts it took, or what
a provider answered. A test asserts that structurally against the compiled contract.
Notifications now consumes typed contact points and endpoints and checks their
validity at notification time. All eight criteria have local evidence.
Added
-
@forgegraph/foundation/consent(experimental): consent grants, withdrawal,
supersession and evidence as durable facts, with purpose, processing activity and
data scope as three independent axes.Consentin@forgegraph/runtimegrants,
evidences, withdraws, supersedes and answerseffectiveAt.Valid time and knowledge time are recorded separately, which is the point of the
package. A withdrawal recorded on 1 March but backdated to 1 February gives three
different correct answers: asked on 20 February about 15 February, consent held,
because the withdrawal was not yet known; asked on 5 March about the same moment,
it did not; asked on 5 March about 15 January, it held, because a backdated
withdrawal reaches back only as far as it says. A decision made last Tuesday has to
stay explainable with what was known last Tuesday.Consent is not authorization: these facts are one input to a decision, and a null
result means no permission was recorded rather than a denial. Notification
preference, agreement and entitlement are different facts with different lifecycles,
and a test asserts structurally that none of them has appeared in this contract.This is a bounded valid/knowledge interpretation built from ordinary fields, not an
L0 temporal facet; ConceptIR #75 is what would make those axes language-level.
All eight criteria on #93 have executable local evidence.
Changed
- Breaking: a projection's
latestaggregate now names its ordering field:
latest <field> by <orderField>.latestpreviously returned the greatest value
of the field, so it wasmax; it now returns the value from the contribution with
the greatest ordering field (a required integer, decimal, money, date or datetime
field), with the record id breaking ties. Write order is never inferred. Bare
latest xisE-PROJ-006with the fix-itlatest x by <field>. Bundles from
older compilers keep their previous behaviour. Migrate with, for example,
latest remainingFraction by observedAt. (#198, #207)
Added
- A reference to a record's own resource accepts
"$self"on create and update,
resolved to the id of the record being written, so a required self-reference
(root : Thread @immutable) can be created. Admin import orders a
self-referencing resource's records so in-snapshot targets come first. (#200, #206) flooranddatetimearithmetic in resource expressions evaluate at runtime
(hour := floor(at / 3600) * 3600), and the compiler accepts exactly the
expression functions the runtime implements (E-EXPR-004); a parity test keeps
the two lists equal. Previously any call compiled and then failed every create.
(#196, #205)
Fixed
- An optional
jsonfield's contract schema stays{}(which already admits
null) instead of becoming{"type": ["string", "null"]}, which rejected every
JSON value but strings. D1Storage.listomitsWHEREwhen there are no predicates; a resource with
neither@tenantnor@softDeletelisted without filters failed with a SQLite
syntax error. (#197, #203)- OpenAPI contracts include
nullin theenumof a nullable enum field, so
generated validators accept the null the API returns. (#199, #204) /healthzcan now fail. Both hosts return 503 with the names of the absent
settings when the instance's own configuration implies something it does not
have — an R2 bucket,REGISTRY_TOKEN_SECRET,INSTANCE_AUTHORITY, the Access
team domain and audience tag. It previously returned a constantok, so a
Worker deployed with no secrets reported healthy on every probe while
answering 503 to every authenticated request, which is exactly what happened
toforge.gmac.io..forgegraph.yamlpoints a deployment health check here,
so a probe that could not fail could not detect the only outage this
deployment has had. Only names are reported, never values, and only absence is
checked — a present but invalid signing key is a different failure and
claiming to have checked it here would be the same kind of lie./v2refuses outright whenREGISTRY_TOKEN_SECRETis unset, instead of
challenging the client with aWWW-Authenticaterealm naming a/v2/token
endpoint that does not exist.docker logincould not exit that loop.src/node.tspasses a credential store tocreateApiand applies
migrations/0003_registry_credentials.sql. Registry credentials were
Workers-only by accident, so the panel answered 503 on every Docker
deployment — and on the Playwright suite, which runs this host rather than the
Worker, which is why no test saw it.- README: corrected the release status. It claimed all five required profiles
were certified against this build;RELEASE_MANIFEST.jsonreports
allRequiredProfilesCertified: false, with two profiles' evidence bound to a
superseded build and three carrying none. The manifest's auto-demotion rule
worked; the prose had not followed it down. It also still described 0.3.0 as
the current release.
Added
packages/console/test/worker.test.ts— the first test of the Workers
entrypoint, which had none despite being the only place/v2, R2, D1, the
credential store and the token endpoint are wired together. D1 is real
node:sqliterunning the real migrations, which no test had executed. It
covers the missing-secret and missing-binding cases, and carries a credential
from HTTP Basic through token exchange to an authorized request — a seam every
component of which was tested and none of which was tested together.packages/console/e2e/credentials.spec.ts— issues a credential through the
real UI against the real server. Verified to fail when the wiring above is
removed.
Install the compiler:
brew install gmackorg/tap/forgec # macOS and Linux
cargo install forgegraph-cli # from source
Runtime packages are on npm under @forgegraph/* with build provenance.
Certified profiles and their evidence: RELEASE_MANIFEST.json.