diff --git a/.github/workflows/reusable-quality-gate.yml b/.github/workflows/reusable-quality-gate.yml index 836a1c93..66ffa464 100644 --- a/.github/workflows/reusable-quality-gate.yml +++ b/.github/workflows/reusable-quality-gate.yml @@ -35,4 +35,6 @@ jobs: - run: node src/cli.js docs check - name: Claim registry and research copies are current run: node scripts/claims-status.mjs --check + - name: Diagram sources match their verified renders + run: node scripts/diagrams.mjs check - run: npm pack --dry-run diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml index e5c00617..8a3a214c 100644 --- a/.github/workflows/static.yml +++ b/.github/workflows/static.yml @@ -40,10 +40,14 @@ jobs: mkdir -p _site/status cp landing/index.html _site/index.html cp landing/app.js _site/app.js + cp -R landing/media _site/media cp public/index.html _site/status/index.html # Brand assets referenced by absolute URL from both pages (favicon, # apple-touch-icon, 1200x630 og card). Served from the Pages root. - cp docs/assets/og.png docs/assets/favicon.svg docs/assets/apple-touch-icon.png _site/ + cp docs/assets/og.jpg docs/assets/favicon.svg docs/assets/apple-touch-icon.png _site/ + # Interactive diagrams (/diagrams/): rendered from docs/diagrams/src by the pinned + # Archify commit; a file whose sha256 differs from its validated receipt is refused. + node scripts/diagrams.mjs site _site/diagrams - name: Setup Pages uses: actions/configure-pages@v6 - name: Upload artifact diff --git a/.gitignore b/.gitignore index a7448fe1..5d30f3df 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,6 @@ __pycache__/ # Generated status page — built fresh at deploy (scripts/build-pages.mjs) public/ + +# Build caches: the pinned Archify checkout (scripts/diagrams.mjs), the Pages API cache +.cache/ diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 2201642d..6f9166f3 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -43,24 +43,9 @@ You author the substrate once. `forge sync` compiles that source into each tool' native config. The four layers are how the brain is expressed; the compiler is how it is delivered. -```mermaid -%%{init: {'theme':'base','themeVariables':{'primaryColor':'#201a15','primaryTextColor':'#f2ede7','primaryBorderColor':'#372c22','lineColor':'#f26430','secondaryColor':'#272019','tertiaryColor':'#171310','edgeLabelBackground':'#201a15','clusterBkg':'#171310','clusterBorder':'#4a3b2e','fontFamily':'ui-sans-serif, system-ui, sans-serif','fontSize':'14px'},'flowchart':{'curve':'basis','padding':10,'nodeSpacing':36,'rankSpacing':44}}}%% -flowchart TD - S["source/
rules.json · substrate.json · mcp.json"] -->|"forge sync
content-hash + DO-NOT-EDIT headers"| N["native configs
CLAUDE.md · AGENTS.md · .cursor · .gemini · .aider · …"] - S -. configures .-> L - subgraph L["the four layers"] - direction LR - T["tools
model-invoked skills"] - C["crew
isolated sub-agents"] - G["guards (enforced)
deterministic hooks"] - M["mcp
atlas + substrate server"] - end - K["local events
cortex · recall · reuse · diagnose"] --> LG[("PCM ledger
.forge/ledger/")] - O["independent oracles
tests · CI · human accept/revert"] -->|"move confidence"| LG - LG <-->|"git union-merge, conflict-free"| TM["teammate ledgers"] - classDef accent fill:#f26430,stroke:#f26430,color:#171310; - class G accent; -``` +[![forgekit's architecture: forge sync compiles source/ into native configs for ten tools and configures the four layers (tools, crew, guards, mcp); local events write content-addressed claims to the PCM ledger, independent oracles move their confidence, and teammate ledgers merge through git union-merge](docs/diagrams/system.svg)](https://codewithjuber.github.io/forgekit/diagrams/system.html) + +[Open the interactive diagram](https://codewithjuber.github.io/forgekit/diagrams/system.html): pan, zoom, search, and trace any node. The four layers, brand-named and emitted cross-tool: @@ -88,28 +73,9 @@ checks and returns a single verdict. It composes the individually-callable stage (`preflight`, `route`, `atlas`, `impact`, `reuse`, `context`, `scope`, `lean`, `anchor`, `verify`) into one pre-action contract. -```mermaid -%%{init: {'theme':'base','themeVariables':{'primaryColor':'#201a15','primaryTextColor':'#f2ede7','primaryBorderColor':'#372c22','lineColor':'#f26430','secondaryColor':'#272019','tertiaryColor':'#171310','edgeLabelBackground':'#201a15','clusterBkg':'#171310','clusterBorder':'#4a3b2e','fontFamily':'ui-sans-serif, system-ui, sans-serif','fontSize':'14px'},'flowchart':{'curve':'basis','padding':10,'nodeSpacing':36,'rankSpacing':44}}}%% -flowchart TD - RE["referenced entities"] --> INTAKE - subgraph INTAKE["intake"] - direction LR - PF["preflight
assumption gap"] --> RT["route
cheapest tier"] - end - INTAKE --> ANALYSIS - subgraph ANALYSIS["analysis"] - direction LR - AT["atlas
code graph"] --> IM["impact
blast radius"] --> PT["predict
failing tests"] --> RU["reuse
cache hit?"] - end - ANALYSIS --> SAFETY - subgraph SAFETY["safety + fit"] - direction LR - CX["context
completeness gate"] --> SC["scope
coupled files"] --> ME["memory
recall + lessons"] --> MN["minimality
lean footprint"] --> GA["goal-anchor
drift check"] - end - SAFETY --> VD["verdict"] - classDef accent fill:#f26430,stroke:#f26430,color:#171310; - class VD accent; -``` +[![The pre-action gate: referenced entities pass through intake (preflight, route), analysis (atlas, impact, predict, reuse) and safety and fit (context, scope, memory, minimality, goal-anchor) to one verdict](docs/diagrams/pre-action-gate.svg)](https://codewithjuber.github.io/forgekit/diagrams/pre-action-gate.html) + +[Open the interactive diagram](https://codewithjuber.github.io/forgekit/diagrams/pre-action-gate.html): pan, zoom, search, and trace any node. **blast radius** — the set of files an edit is predicted to impact, read from the code graph. `forge impact` computes it; the pipeline surfaces it before the model touches @@ -146,23 +112,9 @@ claims into `.forge/ledger/`. Because a claim's bytes are a pure function of `(kind, body, scope)`, every replica computes the same identity — so teammate ledgers fold together over plain git with no conflicts. -```mermaid -%%{init: {'theme':'base','themeVariables':{'primaryColor':'#201a15','primaryTextColor':'#f2ede7','primaryBorderColor':'#372c22','lineColor':'#f26430','secondaryColor':'#272019','tertiaryColor':'#171310','edgeLabelBackground':'#201a15','clusterBkg':'#171310','clusterBorder':'#4a3b2e','fontFamily':'ui-sans-serif, system-ui, sans-serif','fontSize':'14px'},'flowchart':{'curve':'basis','padding':10,'nodeSpacing':36,'rankSpacing':44}}}%% -flowchart LR - subgraph EV["local events"] - direction TB - E1["recall / remember"] - E2["cortex lesson"] - E3["reuse mint"] - E4["diagnose"] - end - EV -->|"content-addressed claims"| LG[(".forge/ledger")] - O["independent oracles
tests · CI · human accept/revert"] -->|"append evidence
move confidence"| LG - TM["teammate ledgers"] <-->|"git union-merge
conflict-free"| LG - LG --> RV["merged read view
recall list · lesson inject · brain index"] - classDef accent fill:#f26430,stroke:#f26430,color:#171310; - class LG accent; -``` +[![Proof-carrying memory: local events write claims and independent oracles append evidence to .forge/ledger/; a merged read view feeds the recall list, lesson injection and the brain index; teammate ledgers merge through git union-merge and forge ledger merge](docs/diagrams/ledger-flow.svg)](https://codewithjuber.github.io/forgekit/diagrams/ledger-flow.html) + +[Open the interactive diagram](https://codewithjuber.github.io/forgekit/diagrams/ledger-flow.html): pan, zoom, search, and trace any node. Mechanically: evidence and tombstones are append-only, hash-deduped logs; confidence (`val`) is a decayed Beta posterior moved only by oracles; merge is a join-semilattice @@ -175,6 +127,12 @@ query | ratify | retract | merge | import` (`--personal` for the per-user ledger Decision recorded in [`docs/adr/0006-proof-carrying-memory.md`](docs/adr/0006-proof-carrying-memory.md). +A claim's life, from mint to tombstone: + +[![Claim lifecycle: a minted claim starts uncertain; confirmations raise it to trusted and contradictions lower it; below val 0.35 it goes dormant until a later confirmation; idle, duplicate and dormant claims are archived with a reason and new evidence brings them back; forge ledger retract tombstones a claim permanently; a reworded lesson is minted as a new claim](docs/diagrams/claim-lifecycle.svg)](https://codewithjuber.github.io/forgekit/diagrams/claim-lifecycle.html) + +[Open the interactive diagram](https://codewithjuber.github.io/forgekit/diagrams/claim-lifecycle.html): pan, zoom, search, and trace any node. + ## 4. The reuse / context loop `forge reuse` is a proof-carrying code cache. A generated artifact is only served again @@ -182,19 +140,9 @@ when its evidence still holds — the confidence is above the floor _and_ its at dependencies still resolve. Otherwise it falls through to generation and mints a fresh claim on the way back. -```mermaid -%%{init: {'theme':'base','themeVariables':{'primaryColor':'#201a15','primaryTextColor':'#f2ede7','primaryBorderColor':'#372c22','lineColor':'#f26430','secondaryColor':'#272019','tertiaryColor':'#171310','edgeLabelBackground':'#201a15','clusterBkg':'#171310','clusterBorder':'#4a3b2e','fontFamily':'ui-sans-serif, system-ui, sans-serif','fontSize':'14px'},'flowchart':{'curve':'basis','padding':10,'nodeSpacing':36,'rankSpacing':44}}}%% -flowchart LR - SP["spec"] --> FP["fingerprint
MinHash + LSH"] - FP --> LD["match ladder
exact → near → adapt → miss"] - LD --> GT{"confidence ≥ floor
AND deps resolve?"} - GT -->|"yes"| SV["serve (proof holds)"] - GT -->|"miss"| GN["generate"] - GN -->|"mint claim"| MT[(".forge/ledger")] - MT -.->|"available next time"| FP - classDef accent fill:#f26430,stroke:#f26430,color:#171310; - class SV accent; -``` +[![Reuse cache: a spec is looked up by a lossless exact key, then by MinHash and LSH similarity checked by a semantic guard (operators, numbers, literals); a hit is served only while the proof check holds (confidence at least 0.6) and is flagged as not revalidated when there is no atlas; a match that differs is only an adapt-tier candidate; a miss is generated, verified and minted as a claim into .forge/ledger/](docs/diagrams/reuse-cache.svg)](https://codewithjuber.github.io/forgekit/diagrams/reuse-cache.html) + +[Open the interactive diagram](https://codewithjuber.github.io/forgekit/diagrams/reuse-cache.html): pan, zoom, search, and trace any node. The completeness gate on the retrieval side is `forge context ""`: it pins the required-knowledge set for the edit (`R(edit)`), downgrades items along a compression ladder @@ -217,6 +165,12 @@ only if the checks fire independently. The same check repeated at another point pre-commit, CI on the same diff) is nested, so the residual is `(1−p)(1−c_max)` (formal synthesis §5.3, corrected 2026-09-21). +Where those deterministic checks run in one Claude Code session: + +[![One Claude Code session with forgekit's hooks: SessionStart injects learned lessons and the last handoff; UserPromptSubmit runs cortex and preflight against the cached atlas and returns an advisory; PreToolUse runs protect-paths, cost-budget, doom-loop and cortex pre-edit and allows or denies the call; PostToolUse formats, redacts secrets and captures evidence; Stop runs the completion gate, lean guard and session learner and distills lessons into the ledger](docs/diagrams/hook-sequence.svg)](https://codewithjuber.github.io/forgekit/diagrams/hook-sequence.html) + +[Open the interactive diagram](https://codewithjuber.github.io/forgekit/diagrams/hook-sequence.html): pan, zoom, search, and trace any node. + **The completion gate (Stop, `src/gate.js`).** The only Stop-path guard that may answer: `completion-gate.sh` runs synchronously (the lesson-mining `cortex.sh stop` stays detached and can never block). The changed set is **session-scoped**: files from commits @@ -386,6 +340,12 @@ and the residual stays `(1−p)(1−c_max)`. This rung adds catches only where i Stop hook could not — edits made after the turn ended, a host or session where the Stop hook never ran, or a session whose one Stop block was already spent. +Plain `forge verify`, end to end: + +[![forge verify: suites are planned for the root and every nested package that declares one; the code state (HEAD, staged and unstaged diffs, untracked files) is captured before and after each suite runs in its own directory, and a change during the run makes the result INCOMPLETE; the verdict is PASS, FAIL, INCOMPLETE or NOT_CONFIGURED; a hallucinated-symbol check runs against the atlas; the result is sealed in .forge/provenance.json and a verifier event is appended to .forge/verify-events.jsonl](docs/diagrams/verify-pipeline.svg)](https://codewithjuber.github.io/forgekit/diagrams/verify-pipeline.html) + +[Open the interactive diagram](https://codewithjuber.github.io/forgekit/diagrams/verify-pipeline.html): pan, zoom, search, and trace any node. + **Deep verification (`src/consensus.js`, `forge verify --deep`).** Where plain `verify` asks one oracle (the tests) plus one heuristic, this runs a table of independent lenses and aggregates them with the same noisy-OR risk score `lessons.js` uses, behind a @@ -677,21 +637,21 @@ from the tree it describes. ```mermaid %%{init: {'theme':'base','themeVariables':{'primaryColor':'#201a15','primaryTextColor':'#f2ede7','primaryBorderColor':'#372c22','lineColor':'#f26430','secondaryColor':'#272019','tertiaryColor':'#171310','edgeLabelBackground':'#201a15','clusterBkg':'#171310','clusterBorder':'#4a3b2e','fontFamily':'ui-sans-serif, system-ui, sans-serif','fontSize':'14px'},'flowchart':{'curve':'basis','padding':10,'nodeSpacing':36,'rankSpacing':44}}}%% flowchart LR - test["test
134 files"] + test["test
135 files"] src["src
120 files"] landing["landing
61 files"] research["research
37 files"] bench["bench
6 files"] global["global
5 files"] - scripts["scripts
3 files"] + scripts["scripts
5 files"] docs["docs
1 file"] examples["examples
1 file"] - test -- 284 --> src + test -- 286 --> src bench -- 12 --> src + scripts -- 5 --> src examples -- 4 --> src - scripts -- 3 --> src + test -- 4 --> scripts test -- 3 --> global - test -- 3 --> scripts test -- 2 --> bench src --> global ``` diff --git a/CHANGELOG.md b/CHANGELOG.md index dbaa02d7..4e230459 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,42 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Fixed + +- **Build caches under `.cache/` stay out of the import graph.** `forge impact`, `forge scope` + and the generated repository map walked into `.cache/`, so a fetched tool checkout (such as + the pinned Archify under `.cache/archify/`) showed up as hundreds of project files. +- **The landing page's hero says ten native targets.** It still said nine after OpenClaw joined + the grid. + +### Added + +- **Every diagram is now rendered by [Archify](https://github.com/tt-a1i/archify) from a typed + source.** Thirteen Archify diagrams replace the fifteen hand-written Mermaid diagrams in + README, ARCHITECTURE, ONBOARDING, GUIDE, the substrate docs and the docs site. + - The sources are architecture, workflow, sequence, dataflow and lifecycle schemas in + `docs/diagrams/src/`, each validated at Archify's showcase quality bar. + - The docs embed a dual-theme SVG that links to an interactive page on the Pages site + (`/diagrams/`). There you can pan, zoom, search and trace a node's dependencies. + - `scripts/diagrams.mjs` pins Archify by commit. The Pages build refuses to publish a + rendered page whose sha256 differs from its validated receipt. + - `node scripts/diagrams.mjs check`, now in the CI quality gate, fails when a source + changed without a re-render. `forge docs check` rejects new hand-written Mermaid + diagrams. + - The generated repository map stays Mermaid, because it is laid out from the live import + graph. + + See `docs/diagrams/README.md`. +- **Illustrations for the docs site and the landing page, and a new social card.** + - Seven docs-site pages open with an illustration. + - The landing page's evidence band has a background image, veiled so its text keeps at + least 5.4:1 contrast. It falls back to a flat color when the reader asks for more + contrast. + - The link-preview card is now `og.jpg` (284 KB), rendered from `docs/assets/og.svg` by + `scripts/og-card.mjs`. The script refuses to write the card if any line of text overlaps + the art or leaves the crop-safe area, or if the file exceeds link-preview size limits. + The previous `og.png` was 1.3 MB, over the size many preview scrapers accept. + ## [1.6.0] - 2026-09-26 ### Security diff --git a/ONBOARDING.md b/ONBOARDING.md index e3b60335..9ceded82 100644 --- a/ONBOARDING.md +++ b/ONBOARDING.md @@ -11,18 +11,9 @@ Windsurf, Zed, Continue, and OpenClaw at once. Author it once; every tool reads This page is the fast path: install, configure a repo, do a task, and watch the ledger start paying off on day two. -```mermaid -%%{init: {'theme':'base','themeVariables':{'primaryColor':'#201a15','primaryTextColor':'#f2ede7','primaryBorderColor':'#372c22','lineColor':'#f26430','secondaryColor':'#272019','tertiaryColor':'#171310','edgeLabelBackground':'#201a15','clusterBkg':'#171310','clusterBorder':'#4a3b2e','fontFamily':'ui-sans-serif, system-ui, sans-serif','fontSize':'14px'},'flowchart':{'curve':'basis','padding':10,'nodeSpacing':36,'rankSpacing':44}}}%% -flowchart TD - I["forge init"] --> Cfg["your tools configured
from one source"] - Cfg --> Work["you work as usual"] - Work --> Gate["substrate checks each task:
ask first? · which model? · what breaks?"] - Gate --> Edit["agent edits, with guardrails"] - Edit --> Learn["cortex learns from corrections"] - Learn -.->|next task is smarter| Work - classDef accent fill:#f26430,stroke:#f26430,color:#171310; - class Gate accent; -``` +[![Guided onboarding: forge init configures your tools from one source; then for each task you work as usual, the substrate checks whether to ask first, which model to use and what breaks, the agent edits with guardrails, and cortex learns from corrections so the next task is smarter](docs/diagrams/onboarding-loop.svg)](https://codewithjuber.github.io/forgekit/diagrams/onboarding-loop.html) + +[Open the interactive diagram](https://codewithjuber.github.io/forgekit/diagrams/onboarding-loop.html): pan, zoom, search, and trace any node. ## 1. Install (once) diff --git a/README.md b/README.md index b863c5ae..dad9bf9f 100644 --- a/README.md +++ b/README.md @@ -132,17 +132,9 @@ Forgekit supplies an external reliability layer: Forgekit runs a deterministic substrate before work, lets the external coding agent act, and records evidence from tests, CI, or explicit human correction afterwards. -```mermaid -%%{init: {'theme':'base','themeVariables':{'primaryColor':'#201a15','primaryTextColor':'#f2ede7','primaryBorderColor':'#372c22','lineColor':'#f26430','secondaryColor':'#272019','tertiaryColor':'#171310','edgeLabelBackground':'#201a15','clusterBkg':'#171310','clusterBorder':'#4a3b2e','fontFamily':'ui-sans-serif, system-ui, sans-serif','fontSize':'14px'},'flowchart':{'curve':'basis','padding':10,'nodeSpacing':36,'rankSpacing':44}}}%% -flowchart TD - T["Task"] --> G["Pre-action substrate"] - G -->|"Missing information"| Q["Clarify first"] - Q --> T - G -->|"Enough information"| A["External coding agent acts"] - A --> V["Tests, CI, or human outcome"] - V --> M["Evidence-weighted memory"] - M -.-> G -``` +[![How forgekit fits around a coding agent: a task goes through the pre-action substrate; missing information sends it back to clarify first, enough information lets the agent act, and the outcome is recorded in memory that feeds the next check](docs/diagrams/core-loop.svg)](https://codewithjuber.github.io/forgekit/diagrams/core-loop.html) + +[Open the interactive diagram](https://codewithjuber.github.io/forgekit/diagrams/core-loop.html): pan, zoom, search, and trace any node. Only independent oracles (tests, CI, a human accept/revert) move a memory's confidence — so a wrong lesson decays out instead of ossifying. Full design: diff --git a/docs/GUIDE.md b/docs/GUIDE.md index a11c1d56..0d4cfe60 100644 --- a/docs/GUIDE.md +++ b/docs/GUIDE.md @@ -70,16 +70,9 @@ substrate** (`forge substrate` — the pre-action check). The full argument is t The daily loop — every outcome an oracle observes lands in the team ledger, and the ledger informs the next task: -```mermaid -%%{init: {'theme':'base','themeVariables':{'primaryColor':'#201a15','primaryTextColor':'#f2ede7','primaryBorderColor':'#372c22','lineColor':'#f26430','secondaryColor':'#272019','tertiaryColor':'#171310','edgeLabelBackground':'#201a15','clusterBkg':'#171310','clusterBorder':'#4a3b2e','fontFamily':'ui-sans-serif, system-ui, sans-serif','fontSize':'14px'},'flowchart':{'curve':'basis','padding':10,'nodeSpacing':36,'rankSpacing':44}}}%% -flowchart LR - W["work — substrate pre-checks,
then edit"] --> O["oracles — forge verify ·
imagine --run · CI · human accept/revert"] - O -->|"outcomes move claim val"| L[("team ledger
.forge/ledger/")] - L <-->|"git + forge ledger merge"| T["teammates' ledgers"] - L -->|"lessons · facts · reuse hits"| W - classDef accent fill:#f26430,stroke:#f26430,color:#171310; - class L accent; -``` +[![The team loop: substrate pre-checks, then the edit; oracles (forge verify, forge imagine --run, CI, a human accept or revert) record outcomes that move each claim's value in the team ledger in .forge/ledger/; teammates' ledgers merge through git and forge ledger merge; lessons, facts and reuse hits feed the next pre-check](diagrams/team-loop.svg)](https://codewithjuber.github.io/forgekit/diagrams/team-loop.html) + +[Open the interactive diagram](https://codewithjuber.github.io/forgekit/diagrams/team-loop.html): pan, zoom, search, and trace any node. ```bash cd your-project @@ -677,6 +670,10 @@ The independent check: runs the real test suite and flags edited symbols that ar the codebase (possible hallucinations). This is what turns "the model says it's done" into "the tests say it's done." +[![forge verify: suites are planned for the root and every nested package that declares one; the code state (HEAD, staged and unstaged diffs, untracked files) is captured before and after each suite runs in its own directory, and a change during the run makes the result INCOMPLETE; the verdict is PASS, FAIL, INCOMPLETE or NOT_CONFIGURED; a hallucinated-symbol check runs against the atlas; the result is sealed in .forge/provenance.json and a verifier event is appended to .forge/verify-events.jsonl](diagrams/verify-pipeline.svg)](https://codewithjuber.github.io/forgekit/diagrams/verify-pipeline.html) + +[Open the interactive diagram](https://codewithjuber.github.io/forgekit/diagrams/verify-pipeline.html): pan, zoom, search, and trace any node. + ```console $ forge verify Forge verify diff --git a/docs/UNIVERSAL_ROUTING.md b/docs/UNIVERSAL_ROUTING.md index 6e5e7d0b..c4a123c1 100644 --- a/docs/UNIVERSAL_ROUTING.md +++ b/docs/UNIVERSAL_ROUTING.md @@ -12,6 +12,12 @@ The router code names no vendor, model, tier or threshold, and a test enforces t > > The shipped prior is a useful initialization, not a guarantee about your workload. The universal router is a separately versioned component from the old tiered router whose 62.1% saving was refuted (`research/empirical-refutation/`); neither result transfers to the other. +How a recommendation is chosen, run and learned from: + +[![forge route universal: the task's 12 features and each model's P(solve) and expected cost select the cheapest cascade that meets the objective, or report INFEASIBLE with a labeled least-bad fallback; each attempt is checked by tests or forge verify, a failure escalates to the next model, and forge route outcome records each outcome for forge route fit, which refits a local estimate shrunk toward the shipped prior](diagrams/router-cascade.svg)](https://codewithjuber.github.io/forgekit/diagrams/router-cascade.html) + +[Open the interactive diagram](https://codewithjuber.github.io/forgekit/diagrams/router-cascade.html): pan, zoom, search, and trace any node. + ## The model **1. Who solves what: multidimensional item response theory.** diff --git a/docs/assets/README.md b/docs/assets/README.md new file mode 100644 index 00000000..f354efcd --- /dev/null +++ b/docs/assets/README.md @@ -0,0 +1,29 @@ +# Brand and image assets + +| File | Used by | How it is made | +| --- | --- | --- | +| `favicon.svg`, `apple-touch-icon.png` | Every page of the Pages site; the Pages build serves them from the site root. | Vector mark | +| `hero-light.svg`, `hero-dark.svg` | The README hero, one per GitHub theme. | Vector | +| `og.svg` | The source of the social card: wordmark, headline and subline over `og-background.webp`. | Edited by hand | +| `og-background.webp` | The card's art: a girih lattice and khatam star on the right, leaving the left side empty for type. | Generated art, 2400×1260 | +| `og.jpg` | `og:image` and `twitter:image` on the landing and status pages. | `node scripts/og-card.mjs`. Never edit it directly. | + +To change the card, edit `og.svg` and run `node scripts/og-card.mjs`. The script uses the +headless Chrome from the pinned Archify checkout (see `docs/diagrams/README.md`) and needs +the network once, to fetch the Inter and JetBrains Mono web fonts. It refuses to write the +card if a font did not load, if any line of text crosses into the art (x > 610 of 1200) or +out of the crop-safe margins, or if the JPEG exceeds the roughly 300 KB that link-preview +scrapers accept. `--check` measures the layout without writing anything. + +The other generated art lives next to the page that uses it: + +- `landing/media/evidence-bg.webp`: the landing page's evidence band. A 55% veil keeps its + text at 5.4:1 contrast or better, and it is dropped under `prefers-contrast: more`. +- `mintlify/images/illustrations/*.webp`: the opening illustrations of seven docs-site + pages. + +All of this art was generated for this project in September 2026 as text-free geometric +illustration in the brand palette (`brand.json`). It contains no sacred script. It +illustrates ideas; it is not data. The diagrams are rendered from typed sources in +[`docs/diagrams`](../diagrams/README.md), and every number the site states comes from the +reports in the repository. diff --git a/docs/assets/og-background.webp b/docs/assets/og-background.webp new file mode 100644 index 00000000..1be4ccb4 Binary files /dev/null and b/docs/assets/og-background.webp differ diff --git a/docs/assets/og.jpg b/docs/assets/og.jpg new file mode 100644 index 00000000..7de849c4 Binary files /dev/null and b/docs/assets/og.jpg differ diff --git a/docs/assets/og.png b/docs/assets/og.png deleted file mode 100644 index bd21401d..00000000 Binary files a/docs/assets/og.png and /dev/null differ diff --git a/docs/assets/og.svg b/docs/assets/og.svg index 93761ae2..5b218306 100644 --- a/docs/assets/og.svg +++ b/docs/assets/og.svg @@ -1,9 +1,8 @@ - + + - - - - @@ -11,31 +10,27 @@ - + - - - F - forgekit + + + F + forgekit - ONE BRAIN FOR EVERY AI CODING AGENT + OPEN SOURCE · BETA · MIT - Give every coding agent - the same working memory. + One operating + memory for every + coding agent. - Proof-carrying memory · impact foresight · reuse · enforced guardrails — - compiled to native config for the agents your team already runs. + Evidence-referenced memory, change-impact + analysis and explicit verification for Claude + Code, Codex, Cursor, Gemini and more. - - - MIT - · - zero runtime dependencies - · - Claude Code, Codex, Cursor, Gemini, Aider & more - + + zero runtime dependencies · node ≥ 20 diff --git a/docs/cognitive-substrate/README.md b/docs/cognitive-substrate/README.md index 7d00d1ec..d7007cba 100644 --- a/docs/cognitive-substrate/README.md +++ b/docs/cognitive-substrate/README.md @@ -202,19 +202,9 @@ Set **`FORGE_LLM=1`** to add a **thin, opt-in semantic layer** on top: a cheap ` call proposes a completeness reading (M2), a complexity band (M1), the coupled edges the regex graph misses (impact), and whether an off-goal file actually serves the goal (M4). -```mermaid -%%{init: {'theme':'base','themeVariables':{'primaryColor':'#201a15','primaryTextColor':'#f2ede7','primaryBorderColor':'#372c22','lineColor':'#f26430','secondaryColor':'#272019','tertiaryColor':'#171310','edgeLabelBackground':'#201a15','clusterBkg':'#171310','clusterBorder':'#4a3b2e','fontFamily':'ui-sans-serif, system-ui, sans-serif','fontSize':'14px'},'flowchart':{'curve':'basis','padding':10,'nodeSpacing':36,'rankSpacing':44}}}%% -flowchart LR - T["task / edit"] --> R["deterministic rubric"] - T --> P["LLM proposer"] - R --> C{reconcile} - P --> V["verify: rubric band · repo grounding
grep · tests"] - V --> C - C -->|passes checks| M["verdict moves
llm-cleared / lowered / raised / verified"] - C -->|fails / unavailable| D["verdict holds
deterministic"] - classDef accent fill:#f26430,stroke:#f26430,color:#171310; - class C accent; -``` +[![Rubric first, LLM second: a task or edit goes to the deterministic rubric and, when FORGE_LLM=1, to an LLM proposer whose proposal is verified against the rubric band, repository grounding (grep) and tests; the verdict moves (llm-cleared, lowered, raised or verified) only when the proposal passes those checks, and otherwise the deterministic verdict holds](../diagrams/llm-reconcile.svg)](https://codewithjuber.github.io/forgekit/diagrams/llm-reconcile.html) + +[Open the interactive diagram](https://codewithjuber.github.io/forgekit/diagrams/llm-reconcile.html): pan, zoom, search, and trace any node. The model **proposes**; the deterministic rubric, the code graph, and the tests **verify**. The verdict only moves when the proposal survives that check — otherwise it falls back, unchanged. diff --git a/docs/diagrams/README.md b/docs/diagrams/README.md new file mode 100644 index 00000000..4c3bf1d3 --- /dev/null +++ b/docs/diagrams/README.md @@ -0,0 +1,83 @@ +# Diagrams + +Every diagram in this repository's docs, the docs site and the GitHub Pages site is +rendered by [Archify](https://github.com/tt-a1i/archify) from a typed JSON source in this +folder. Nobody hand-draws a diagram or hand-edits a rendered file. `forge docs check` +rejects a hand-written Mermaid block in any tracked Markdown or MDX file, and it rejects +an embed that names a diagram the manifest does not register. + +**Browse them:** https://codewithjuber.github.io/forgekit/diagrams/. Each diagram opens as +an interactive page where you can pan, zoom and search (`/`). Press `?` for the guide, and +select a node to trace what it depends on and what depends on it. + +## Layout + +| Path | What it is | Edited by | +| --- | --- | --- | +| `src/..json` | The source: one of Archify's five typed schemas (`architecture`, `workflow`, `sequence`, `dataflow`, `lifecycle`). | You | +| `.svg` | The static export, embedded by the Markdown docs. It is dual-theme and follows the reader's light or dark preference. | `build` | +| `../../mintlify/images/diagrams/.svg` | Byte-identical copies for the docs site, which can only serve files under `mintlify/`. Git stores each identical file once. | `build` / `sync` | +| `diagrams.json` | The manifest. For each diagram it records the id, type, title, the files that embed it (`usedIn`), and the receipt of its last verified render. | `build` (you edit `usedIn`) | + +The interactive HTML pages are about 0.8 MB each and are not committed. The Pages workflow +renders them from the pinned Archify commit. A given source and commit always produce the +same bytes, so the workflow checks each page's sha256 against its receipt in +`diagrams.json`. It refuses to publish a page that differs, so the site serves exactly what +was validated here. + +## Changing a diagram + +1. Edit `src/..json`. The schemas, examples and authoring rules are in the + pinned checkout, under `archify/SKILL.md`, `archify/schemas/` and `archify/examples/`. +2. Render it: + + ```sh + node scripts/diagrams.mjs build + ``` + + This validates the source at Archify's showcase quality bar, renders it, exports the SVG + through the viewer's own Export menu (in headless Chrome, using Archify's own driver), + refreshes the docs-site copy and rewrites the receipt. The first run fetches the pinned + Archify commit into `.cache/archify/`. To use an existing checkout of that commit, set + `FORGE_ARCHIFY_DIR`. To choose the browser, set `ARCHIFY_CHROME`. +3. Commit the source, the SVG, its `mintlify/images/diagrams/` copy and `diagrams.json` + together. + +To add a diagram, create its source and add an entry `{ "id", "type", "usedIn": [] }` to +`diagrams.json`. Then run `build`, embed the SVG, and list each file that embeds it in +`usedIn`. The check verifies that every listed file really does embed the diagram. + +## Embedding + +- **Markdown (GitHub, npm):** link the SVG to its interactive page: + + ```md + [![Alt text that says what the diagram shows](docs/diagrams/core-loop.svg)](https://codewithjuber.github.io/forgekit/diagrams/core-loop.html) + ``` + +- **Docs site (Mintlify):** put the copy in a ``, then add a link to the interactive + page. +- **Generated diagrams are the exception.** The repository map in `ARCHITECTURE.md` stays + Mermaid because `forge docs render` regenerates it from the live import graph. Archify + lays out what an author specifies; it does not lay out a graph automatically. The check + allows Mermaid inside `forge:render` markers. + +## Checks + +- `node scripts/diagrams.mjs check` runs offline in CI. It fails when any of these is true: + - a source changed since its receipt was written + - a source is not registered in the manifest + - an SVG or a docs-site copy is missing or stale + - a `usedIn` file no longer embeds its diagram + - the receipts were written by an Archify commit other than the pinned one +- `node scripts/diagrams.mjs sync` refreshes the docs-site copies without rendering. +- `node scripts/diagrams.mjs site ` is what the Pages workflow runs. It renders every + page, verifies each against its receipt, and writes the gallery. + +## Credits + +- **Archify** is © tt-a1i and © Cocoon AI, under the MIT License. It is pinned by commit + in `scripts/diagrams.mjs`; bumping the pin means re-running `build` for every diagram. +- The SVG exports embed subsets of **JetBrains Mono** (© The JetBrains Mono Project + Authors), which is under the SIL Open Font License 1.1. Each export carries the license + text in its font CSS. diff --git a/docs/diagrams/claim-lifecycle.svg b/docs/diagrams/claim-lifecycle.svg new file mode 100644 index 00000000..197d15ef --- /dev/null +++ b/docs/diagrams/claim-lifecycle.svg @@ -0,0 +1,616 @@ + + + Claim lifecycle in the ledger + A lifecycle diagram generated by Archify. + + + + + + + + + + + + + + + + + + + + + + + + + 01 / Live · retrievable + + 02 / Out of retrieval + + 03 / Permanent + + + + + + + + + + + + + + + + + + + Reworded lesson · keeps trust only if equivalent · Live · retrievable · up to case · space · punct. + + + + Reworded lesson + keeps trust only if equivalent + up to case · space · punct. + + + + Minted · 0.5 prior · Live · retrievable · candidate + + + + Minted + 0.5 prior + candidate + + + + Uncertain · val 0.35–0.65 · Live · retrievable + + + + Uncertain + val 0.35–0.65 + + + + Trusted · val ≥ 0.65 · Live · retrievable · reuse serve floor 0.6 + + + + Trusted + val ≥ 0.65 + reuse serve floor 0.6 + + + + Dormant · val < 0.35 · latched · Out of retrieval · kept for audit, never retrieved + + + + Dormant + val < 0.35 · latched + kept for audit, never retrieved + + + + Archived · .forge/ledger/attic/ · Out of retrieval · tombstoned · dormant · idle · duplicate + + + + Archived + .forge/ledger/attic/ + tombstoned · dormant · idle · duplicate + + + + Tombstoned · permanent · Permanent · retracted by a human + + + + Tombstoned + permanent + retracted by a human + + + + + + + + confirms raise val + + + + contradicts lower val + + + + contradicts: val < 0.35 + + + + later confirmation + + + + dormant + + + + idle · duplicate + past learned cut-off · survivor named + + + + forge ledger retract + + + + new evidence brings it back + + + + + Legend + + + start + + + + active state + + + + failure / exit + + + + neutral + + + + external + + + \ No newline at end of file diff --git a/docs/diagrams/core-loop.svg b/docs/diagrams/core-loop.svg new file mode 100644 index 00000000..76282c09 --- /dev/null +++ b/docs/diagrams/core-loop.svg @@ -0,0 +1,582 @@ + + + How forgekit fits around a coding agent + A workflow diagram generated by Archify. + + + + + + + + + + + + + + + + + + + + + + + + + 01 / Task + + + 02 / forgekit + + + 03 / Agent and outcome + + + + + + + + + + + + + + + + + + + Clarify first · Task + + + + Clarify first + + + + Task · Task + + + + Task + + + + Pre-action substrate · forgekit + + + + Pre-action substrate + + + + Memory · evidence-weighted · forgekit + + + + Memory + evidence-weighted + + + + Agent acts · external coding agent · Agent and outcome + + + + Agent acts + external coding agent + + + + Tests, CI or human · Agent and outcome + + + + Tests, CI or human + + + + + + + + enough information + + + + feeds the next check + + + + missing information + + + + + + + Legend + + + User UI + + + + Agent logic + + + + Policy + + + + Context / trace + + + + External system + + + \ No newline at end of file diff --git a/docs/diagrams/diagrams.json b/docs/diagrams/diagrams.json new file mode 100644 index 00000000..2fbb78ce --- /dev/null +++ b/docs/diagrams/diagrams.json @@ -0,0 +1,202 @@ +{ + "archify": { + "repo": "https://github.com/tt-a1i/archify", + "commit": "9e35d2b0b39b155553ba9fcfe0b4f2a5198dd993", + "license": "MIT" + }, + "diagrams": [ + { + "id": "core-loop", + "type": "workflow", + "usedIn": [ + "README.md" + ], + "title": "How forgekit fits around a coding agent", + "receipt": { + "specSha256": "36f083e0f0fd475928450143da634566b42b1d461e3c644df19ec7425cb37494", + "artifactSha256": "6d593c7f5fb0f2276a64840a58b4d92aecdcbdf792e3d1d283464aa610520673", + "artifactBytes": 802431, + "checks": "9/9 showcase" + } + }, + { + "id": "system", + "type": "architecture", + "usedIn": [ + "ARCHITECTURE.md", + "mintlify/concepts/config-compiler.mdx" + ], + "title": "forgekit: one source, native configs, enforced guards, evidence-carrying memory", + "receipt": { + "specSha256": "fa81768a8a95ab952a1bfb9ba2bb2968074b90142d9b9a5e5ca717001408fba6", + "artifactSha256": "9c7108271573d07626a1af3c9ce31816ce7c1d99874fd4fda768687c7e7e89a6", + "artifactBytes": 810944, + "checks": "9/9 showcase" + } + }, + { + "id": "pre-action-gate", + "type": "workflow", + "usedIn": [ + "ARCHITECTURE.md", + "mintlify/concepts/pre-action-gate.mdx" + ], + "title": "The pre-action gate: one deterministic pass before the agent edits", + "receipt": { + "specSha256": "01f9114c08733345bd946316c398e67a77cd72ba45d187dfde62cf0ae4809a8d", + "artifactSha256": "a4628c3c21c9f1d8be3365de05e33a5a19920471eb7021b33bda24b6055b157a", + "artifactBytes": 810094, + "checks": "9/9 showcase" + } + }, + { + "id": "ledger-flow", + "type": "dataflow", + "usedIn": [ + "ARCHITECTURE.md", + "mintlify/concepts/proof-carrying-memory.mdx", + "mintlify/guides/team-memory.mdx" + ], + "title": "Proof-carrying memory: how claims gain and lose trust", + "receipt": { + "specSha256": "e2468b369423327a7a4b39def20dc2c3b61e198367f1f922e8aa456cea173e80", + "artifactSha256": "6778222c587e62d1e088b81af1c2d90348801410e2e3da0f968f4cb973b3f973", + "artifactBytes": 808572, + "checks": "9/9 showcase" + } + }, + { + "id": "claim-lifecycle", + "type": "lifecycle", + "usedIn": [ + "ARCHITECTURE.md", + "mintlify/concepts/proof-carrying-memory.mdx" + ], + "title": "Claim lifecycle in the ledger", + "receipt": { + "specSha256": "90ec1d01391f010752f8df9204194a18e405fc8e5887107763a6f4256bc06611", + "artifactSha256": "e0d31bdb3007eb87b2b7c3273777b9f7f8951c6bb804685e2c89d082142f0206", + "artifactBytes": 808845, + "checks": "9/9 showcase" + } + }, + { + "id": "reuse-cache", + "type": "workflow", + "usedIn": [ + "ARCHITECTURE.md", + "mintlify/concepts/proof-carrying-memory.mdx" + ], + "title": "Reuse cache: serve only while the proof holds", + "receipt": { + "specSha256": "2a8c4e773fc7d91dacb3de2964f3c3b08999b2bc334348807c02df82f8b3a2fe", + "artifactSha256": "6956ed90f594f32323f63684eec1fe67aa1f55cc7ced32501259f171c7f5bdcf", + "artifactBytes": 813029, + "checks": "9/9 showcase" + } + }, + { + "id": "team-loop", + "type": "workflow", + "usedIn": [ + "docs/GUIDE.md" + ], + "title": "The team loop: work, verify, remember", + "receipt": { + "specSha256": "6bf73f5500964bc19185cb52bd79f354243cd71cec920a44141eb499f1b2201e", + "artifactSha256": "e90ff6b2bb13ec4efa6ce8566a9dd71c1307c247be0c0006f71aae1d45483bb5", + "artifactBytes": 801007, + "checks": "9/9 showcase" + } + }, + { + "id": "hook-sequence", + "type": "sequence", + "usedIn": [ + "ARCHITECTURE.md", + "mintlify/concepts/cross-session-memory.mdx" + ], + "title": "One Claude Code session with forgekit's hooks", + "receipt": { + "specSha256": "7d64581815896d8bc7b02ed3a083a2f79f1003d59e80504a5529ddb29efec171", + "artifactSha256": "63c7f944ef3641c474fcda162e6c873da2bfd43acd98a66b19d357936905dce2", + "artifactBytes": 810635, + "checks": "9/9 showcase" + } + }, + { + "id": "verify-pipeline", + "type": "workflow", + "usedIn": [ + "ARCHITECTURE.md", + "docs/GUIDE.md", + "mintlify/concepts/verification-gates.mdx" + ], + "title": "forge verify: a PASS bound to the code that was tested", + "receipt": { + "specSha256": "99a2dc93be3d1bba13bbfd4c16aef7e9def0a922e1991d202077dbb12bfeffd4", + "artifactSha256": "c574188d958ec8493a57989fc868e6b5edd4e13c92d6c1d542dae611ed72cfcc", + "artifactBytes": 807339, + "checks": "9/9 showcase" + } + }, + { + "id": "onboarding-loop", + "type": "workflow", + "usedIn": [ + "ONBOARDING.md", + "mintlify/guides/zero-config-onboarding.mdx" + ], + "title": "Guided onboarding: what happens after forge init", + "receipt": { + "specSha256": "b16e5c655a469284374caa8c04ab570882ec2f65a67ae48efb45113a638c702d", + "artifactSha256": "906dfad571e5d6b7b0e13cf6ac558a136461841b21637126046873123399674b", + "artifactBytes": 801851, + "checks": "9/9 showcase" + } + }, + { + "id": "llm-reconcile", + "type": "workflow", + "usedIn": [ + "docs/cognitive-substrate/README.md" + ], + "title": "Rubric first, LLM second: how a proposal can move a verdict", + "receipt": { + "specSha256": "ad240c992e5927c0f41b06467ec2e09600ed30d28603195d29f2e526912ec0fa", + "artifactSha256": "a97271ef516163b9e21a330b19099bb5ff62b1f52d15e68dbb2bec7cf748c0ac", + "artifactBytes": 803139, + "checks": "9/9 showcase" + } + }, + { + "id": "router-cascade", + "type": "workflow", + "usedIn": [ + "docs/UNIVERSAL_ROUTING.md", + "mintlify/concepts/model-routing.mdx" + ], + "title": "forge route universal: a cascade under an explicit objective", + "receipt": { + "specSha256": "900d1d09ee7910b349bf9a5284f69d2f6c81f0522a36582a1845ef03ba8835b7", + "artifactSha256": "2a6b7cb3ad4d601b01f41aee5376b8d308cee5d625416e7e3088891e2c5aa2ec", + "artifactBytes": 812232, + "checks": "9/9 showcase" + } + }, + { + "id": "phase-map", + "type": "architecture", + "usedIn": [ + "docs/plans/substrate-v2/00-overview.md" + ], + "title": "Substrate v2 plan: phases and dependencies", + "receipt": { + "specSha256": "df6ce832e4d597f41827d041fa3b15ddd950324bc4c69fd48875e4a042721445", + "artifactSha256": "064cd5d36912d29aa49d04f8d924333f83da69dbad48f2b25b7f636eee9b6f45", + "artifactBytes": 804443, + "checks": "9/9 showcase" + } + } + ] +} diff --git a/docs/diagrams/hook-sequence.svg b/docs/diagrams/hook-sequence.svg new file mode 100644 index 00000000..9eee0d3e --- /dev/null +++ b/docs/diagrams/hook-sequence.svg @@ -0,0 +1,662 @@ + + + One Claude Code session with forgekit's hooks + A sequence diagram generated by Archify. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SessionStart: recall-load · cortex session-start + + + + + + + + inject learned lessons + last handoff (.forge/state.md) + + + + + + + + prompt + + + + + + + + UserPromptSubmit: cortex prompt + preflight + + + + + + + + reads CACHED atlas only; never builds it in a hook + + + + + + + + substrate advisory: suggested model · impact · verify checklist + + + + + + + + PreToolUse: protect-paths · cost-budget · doom-loop · cortex pre-edit + + + + + + + + allow | deny + + + + + + + + PostToolUse: format-on-edit · secret-redact · cortex capture + + + + + + + + cortex capture: signals + evidence + + + + + + + + Stop: completion-gate · lean-guard · session-learner · cortex stop + + + + + + + + cortex stop: distill lessons into the ledger + + + + + + + + session ends | agent told what is missing + + + + + + + Session start + + + + Prompt + + + + Tool call + + + + Stop + + + + + Developer · Sequence participant + + + + Developer + + + + Claude Code (agent) · Sequence participant + + + + Claude Code (agent) + + + + forgekit hooks · Sequence participant + + + + forgekit hooks + + + + .forge/ state · Sequence participant + + + + .forge/ state + + + + + Legend + + + request + + + + return + + + + security + + + + default message + + + \ No newline at end of file diff --git a/docs/diagrams/ledger-flow.svg b/docs/diagrams/ledger-flow.svg new file mode 100644 index 00000000..d629008b --- /dev/null +++ b/docs/diagrams/ledger-flow.svg @@ -0,0 +1,623 @@ + + + Proof-carrying memory: how claims gain and lose trust + A data-flow diagram generated by Archify. + + + + + + + + + + + + + + + + + + + + + + + + + 01 / This machine · sources + + + 02 / This machine · ledger + + + 03 / This machine · read + + + 04 / This machine · feeds + + + 05 / Team · git + + + + + + + + + + + + + + Independent oracles · tests · CI · 01 / This machine · sources · human accept / revert + + + + Independent oracles + tests · CI + human accept / revert + + + + Local events · recall / remember · cortex lesson · 01 / This machine · sources · reuse mint · diagnose + + + + Local events + recall / remember · cortex lesson + reuse mint · diagnose + + + + .forge/ledger/ · content-addressed claims · 02 / This machine · ledger · append-only evidence logs + + + + .forge/ledger/ + content-addressed claims + append-only evidence logs + + + + Merged read view · 03 / This machine · read + + + + Merged read view + + + + Recall list · 04 / This machine · feeds + + + + Recall list + + + + Lesson injection · 04 / This machine · feeds + + + + Lesson injection + + + + Brain index · 04 / This machine · feeds + + + + Brain index + + + + Teammate ledgers · 05 / Team · git + + + + Teammate ledgers + + + + + + writes claims + + + + append evidence + moves confidence + + + + claims + evidence + + + + feeds + + + + feeds + + + + feeds + + + + git union-merge + conflict-free + + + + forge ledger merge / sync + + + + + Legend + + + primary data + + + + data store + + + + data flow + + + \ No newline at end of file diff --git a/docs/diagrams/llm-reconcile.svg b/docs/diagrams/llm-reconcile.svg new file mode 100644 index 00000000..d1e0ca70 --- /dev/null +++ b/docs/diagrams/llm-reconcile.svg @@ -0,0 +1,581 @@ + + + Rubric first, LLM second: how a proposal can move a verdict + A workflow diagram generated by Archify. + + + + + + + + + + + + + + + + + + + + + + + + + 01 / Rubric first + + + 02 / Reconcile + + + 03 / LLM second + + + + + + + + + + + + + + + + + + + Deterministic rubric · Rubric first + + + + Deterministic rubric + + + + Verdict holds · deterministic · Rubric first + + + + Verdict holds + deterministic + + + + Task or edit · Reconcile + + + + Task or edit + + + + Reconcile · Reconcile + + + + Reconcile + + + + Verdict moves · llm-cleared / lowered / raised / verified · Reconcile + + + + Verdict moves + llm-cleared / lowered / raised / verified + + + + LLM proposer · opt-in: FORGE_LLM=1 · LLM second · never judges alone + + + + LLM proposer + opt-in: FORGE_LLM=1 + never judges alone + + + + Verify · rubric band · repo grounding (grep) · tests · LLM second + + + + Verify + rubric band · repo grounding (grep) · tests + + + + + + fails or unavailable + + + + passes checks + + + + + + + + + + Legend + + + Policy + + + + Context / trace + + + + Cloud service + + + \ No newline at end of file diff --git a/docs/diagrams/onboarding-loop.svg b/docs/diagrams/onboarding-loop.svg new file mode 100644 index 00000000..c99ed494 --- /dev/null +++ b/docs/diagrams/onboarding-loop.svg @@ -0,0 +1,576 @@ + + + Guided onboarding: what happens after forge init + A workflow diagram generated by Archify. + + + + + + + + + + + + + + + + + + + + + + + + + 01 / Setup + + + 02 / Each task + + + 03 / Learning + + + + + + + + + + + + + + + + + + forge init · Setup + + + + forge init + + + + Your tools configured · from one source · Setup + + + + Your tools configured + from one source + + + + You work as usual · Each task + + + + You work as usual + + + + Substrate checks · ask first? which model? what breaks? · Each task + + + + Substrate checks + ask first? which model? what breaks? + + + + Agent edits · with guardrails · Each task + + + + Agent edits + with guardrails + + + + Cortex learns · from corrections · Learning + + + + Cortex learns + from corrections + + + + + + + + + + next task is smarter + + + + + + Legend + + + User UI + + + + Agent logic + + + + Policy + + + + Tool action + + + + Context / trace + + + \ No newline at end of file diff --git a/docs/diagrams/phase-map.svg b/docs/diagrams/phase-map.svg new file mode 100644 index 00000000..c9f9da53 --- /dev/null +++ b/docs/diagrams/phase-map.svg @@ -0,0 +1,583 @@ + + + Substrate v2 plan: phases and dependencies + An architecture diagram generated by Archify. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + P0 specs · done · Architecture component + + + + P0 specs + done + + + + P1 ledger core · done · Architecture component + + + + P1 ledger core + done + + + + P2 team sync · done · Architecture component + + + + P2 team sync + done + + + + P6 UI quality gate · done · Architecture component + + + + P6 UI quality gate + done + + + + P4 context assembly · partial · Architecture component + + + + P4 context assembly + partial + + + + P3 reuse cache · done · Architecture component + + + + P3 reuse cache + done + + + + P5 loop closure · done · Architecture component + + + + P5 loop closure + done + + + + P7 dashboard · done · Architecture component + + + + P7 dashboard + done + + + + P8 evaluation · partial · Architecture component + + + + P8 evaluation + partial + + + + + + + + + + + + + + + + + + + + + Legend + + + Backend + + + \ No newline at end of file diff --git a/docs/diagrams/pre-action-gate.svg b/docs/diagrams/pre-action-gate.svg new file mode 100644 index 00000000..841305b4 --- /dev/null +++ b/docs/diagrams/pre-action-gate.svg @@ -0,0 +1,656 @@ + + + The pre-action gate: one deterministic pass before the agent edits + A workflow diagram generated by Archify. + + + + + + + + + + + + + + + + + + + + + + + + + 01 / Intake + + + 02 / Analysis + + + 03 / Safety + fit + + + + + + + + + + + + + + + + + + + + + + + + Referenced entities · from the task · Intake + + + + Referenced entities + from the task + + + + preflight · assumption gap · Intake + + + + preflight + assumption gap + + + + route · cheapest capable tier · Intake + + + + route + cheapest capable tier + + + + atlas · code graph · Analysis + + + + atlas + code graph + + + + impact · blast radius · Analysis + + + + impact + blast radius + + + + predict · likely failing tests · Analysis + + + + predict + likely failing tests + + + + reuse · cache hit? · Analysis + + + + reuse + cache hit? + + + + Verdict · advisory by default · Safety + fit + + + + Verdict + advisory by default + + + + goal-anchor · drift check · Safety + fit + + + + goal-anchor + drift check + + + + minimality · lean footprint · Safety + fit + + + + minimality + lean footprint + + + + memory · recall + lessons · Safety + fit + + + + memory + recall + lessons + + + + scope · coupled files · Safety + fit + + + + scope + coupled files + + + + context · completeness gate · Safety + fit + + + + context + completeness gate + + + + + + + + + + + + + + + + + + + Legend + + + Policy + + + + Context / trace + + + \ No newline at end of file diff --git a/docs/diagrams/reuse-cache.svg b/docs/diagrams/reuse-cache.svg new file mode 100644 index 00000000..d4c78b53 --- /dev/null +++ b/docs/diagrams/reuse-cache.svg @@ -0,0 +1,668 @@ + + + Reuse cache: serve only while the proof holds + A workflow diagram generated by Archify. + + + + + + + + + + + + + + + + + + + + + + + + + 01 / Lookup ladder + + + 02 / Serve gate + + + + EX / Miss path + + + + + + + + + + + + + + + + + + + + + + + Spec · the task text · Lookup ladder + + + + Spec + the task text + + + + Exact tier · lossless except whitespace · Lookup ladder · Unicode-normalized; case kept + + + + Exact tier + lossless except whitespace + Unicode-normalized; case kept + + + + Near tier · MinHash + LSH similarity · Lookup ladder + + + + Near tier + MinHash + LSH similarity + + + + Semantic guard · operators · numbers · literals · Lookup ladder · identifiers · paths · negation + + + + Semantic guard + operators · numbers · literals + identifiers · paths · negation + + + + Adapt tier · a match that differs · Lookup ladder + + + + Adapt tier + a match that differs + + + + Proof check · serve floor: confidence ≥ 0.6 · Serve gate · same sha256 · dep contracts hold + + + + Proof check + serve floor: confidence ≥ 0.6 + same sha256 · dep contracts hold + + + + Served · proof holds · adds no evidence · Serve gate · no atlas → requiresRevalidation + + + + Served + proof holds · adds no evidence + no atlas → requiresRevalidation + + + + forge reuse mint · claim → .forge/ledger/ · Miss path · available next time + + + + forge reuse mint + claim → .forge/ledger/ + available next time + + + + Verify · Miss path + + + + Verify + + + + Generate · Miss path + + + + Generate + + + + + + otherwise: miss + + + + fails + + + + all hold + + + + hit + + + + otherwise + + + + + differs + + + + agrees: hit + + + + lookup + + + + similar + + + + + + Legend + + + Agent logic + + + + Policy + + + + Tool action + + + + Context / trace + + + + External system + + + \ No newline at end of file diff --git a/docs/diagrams/router-cascade.svg b/docs/diagrams/router-cascade.svg new file mode 100644 index 00000000..307cc53f --- /dev/null +++ b/docs/diagrams/router-cascade.svg @@ -0,0 +1,646 @@ + + + forge route universal: a cascade under an explicit objective + A workflow diagram generated by Archify. + + + + + + + + + + + + + + + + + + + + + + + + + + EX / Fallbacks + + + 02 / Route (advice) + + + 03 / Run the cascade + + + 04 / Record + fit + + + + + + + + + + + + + + + + + + + + + + + INFEASIBLE · exit 1 · labeled least-bad fallback · Fallbacks + + + + INFEASIBLE · exit 1 + labeled least-bad fallback + + + + Escalate · next model in the cascade · Fallbacks + + + + Escalate + next model in the cascade + + + + forge route universal · "<task>" → 12 task features · Route (advice) + + + + forge route universal + "<task>" → 12 task features + + + + Per model · P(solve) + expected cost · Route (advice) + + + + Per model + P(solve) + expected cost + + + + Cheapest cascade · that meets the objective · Route (advice) + + + + Cheapest cascade + that meets the objective + + + + Recommendation · advice only if no provider id · Route (advice) + + + + Recommendation + advice only if no provider id + + + + Attempt 1 · first model in cascade · Run the cascade + + + + Attempt 1 + first model in cascade + + + + Check · tests / forge verify · Run the cascade + + + + Check + tests / forge verify + + + + Done · Run the cascade + + + + Done + + + + forge route outcome · self-reported unless --verify-run · Record + fit + + + + forge route outcome + self-reported unless --verify-run + + + + forge route fit · MAP shrinkage to shipped prior · Record + fit + + + + forge route fit + MAP shrinkage to shipped prior + + + + + + + fail + + + + pass + + + + feasible + + + + infeasible + + + + + + + + + each outcome + + + + + Legend + + + User UI + + + + Agent logic + + + + External system + + + \ No newline at end of file diff --git a/docs/diagrams/src/claim-lifecycle.lifecycle.json b/docs/diagrams/src/claim-lifecycle.lifecycle.json new file mode 100644 index 00000000..9c03f977 --- /dev/null +++ b/docs/diagrams/src/claim-lifecycle.lifecycle.json @@ -0,0 +1,41 @@ +{ + "schema_version": 1, + "diagram_type": "lifecycle", + "meta": { + "title": "Claim lifecycle in the ledger", + "locale": "en", + "quality_profile": "showcase", + "viewBox": [980, 632], + "views": [ + {"id": "gain-trust", "label": "Gaining trust", "focus": ["reworded", "minted", "uncertain", "trusted"], "note": "A new claim starts at the 0.5 prior; confirming evidence raises val toward Trusted."}, + {"id": "lose-trust", "label": "Losing trust", "focus": ["trusted", "uncertain", "dormant", "tombstoned"], "note": "Contradicting evidence lowers val; dormancy latches until a later confirmation; retraction is permanent."}, + {"id": "attic", "label": "Attic and return", "focus": ["dormant", "trusted", "archived", "uncertain"], "note": "Archived claims keep a recorded reason; new evidence brings them back."} + ] + }, + "lanes": [ + {"id": "main", "label": "Live · retrievable"}, + {"id": "out", "label": "Out of retrieval"}, + {"id": "terminal", "label": "Permanent"} + ], + "states": [ + {"id": "reworded", "type": "external", "label": "Reworded lesson", "sublabel": "keeps trust only if equivalent", "tag": "up to case · space · punct.", "lane": "main", "col": 0, "width": 124}, + {"id": "minted", "type": "start", "label": "Minted", "sublabel": "0.5 prior", "tag": "candidate", "lane": "main", "col": 1, "width": 90}, + {"id": "uncertain", "type": "active", "label": "Uncertain", "sublabel": "val 0.35–0.65", "lane": "main", "col": 2}, + {"id": "trusted", "type": "active", "label": "Trusted", "sublabel": "val ≥ 0.65", "tag": "reuse serve floor 0.6", "lane": "main", "col": 4}, + {"id": "dormant", "type": "failure", "label": "Dormant", "sublabel": "val < 0.35 · latched", "tag": "kept for audit, never retrieved", "lane": "out", "col": 0, "width": 130}, + {"id": "archived", "type": "neutral", "label": "Archived", "sublabel": ".forge/ledger/attic/", "tag": "tombstoned · dormant · idle · duplicate", "lane": "out", "col": 2, "width": 164}, + {"id": "tombstoned", "type": "failure", "label": "Tombstoned", "sublabel": "permanent", "tag": "retracted by a human", "lane": "terminal", "col": 1} + ], + "transitions": [ + {"id": "reword", "from": "reworded", "to": "minted"}, + {"id": "mint", "from": "minted", "to": "uncertain"}, + {"id": "confirm", "from": "uncertain", "to": "trusted", "label": "confirms raise val", "variant": "emphasis"}, + {"id": "contradict", "from": "trusted", "to": "uncertain", "label": "contradicts lower val", "labelAt": [556, 202]}, + {"id": "fall", "from": "uncertain", "to": "dormant", "label": "contradicts: val < 0.35", "variant": "security", "labelAt": [322, 233]}, + {"id": "wake", "from": "dormant", "to": "uncertain", "label": "later confirmation", "variant": "emphasis", "labelAt": [470, 233]}, + {"id": "archive-dormant", "from": "dormant", "to": "archived", "label": "dormant", "variant": "dashed"}, + {"id": "archive-idle", "from": "trusted", "to": "archived", "label": "idle · duplicate", "note": "past learned cut-off · survivor named", "variant": "dashed", "labelAt": [710, 233]}, + {"id": "retract", "from": "trusted", "to": "tombstoned", "label": "forge ledger retract", "variant": "security", "fromSide": "right", "toSide": "right", "via": [[830, 157], [830, 479]], "labelAt": [830, 350]}, + {"id": "restore", "from": "archived", "to": "uncertain", "label": "new evidence brings it back", "variant": "emphasis", "fromSide": "bottom", "toSide": "top", "via": [[710, 390], [20, 390], [20, 80], [402, 80]]} + ] +} diff --git a/docs/diagrams/src/core-loop.workflow.json b/docs/diagrams/src/core-loop.workflow.json new file mode 100644 index 00000000..26236c84 --- /dev/null +++ b/docs/diagrams/src/core-loop.workflow.json @@ -0,0 +1,43 @@ +{ + "schema_version": 2, + "diagram_type": "workflow", + "meta": { + "title": "How forgekit fits around a coding agent", + "locale": "en", + "quality_profile": "showcase" + }, + "lanes": [ + { "id": "task-lane", "label": "Task" }, + { "id": "forgekit", "label": "forgekit" }, + { "id": "work-lane", "label": "Agent and outcome" } + ], + "mainPath": ["task", "substrate", "agent", "outcome", "memory"], + "semanticChecks": { + "allowedRoots": [], + "allowedTerminals": [], + "requiredEdges": [ + { "from": "task", "to": "substrate" }, + { "from": "substrate", "to": "clarify" }, + { "from": "clarify", "to": "task" }, + { "from": "substrate", "to": "agent" }, + { "from": "memory", "to": "substrate" } + ] + }, + "nodes": [ + { "id": "clarify", "lane": "task-lane", "col": 0, "type": "frontend", "label": "Clarify first", "width": 180 }, + { "id": "task", "lane": "task-lane", "col": 1, "type": "frontend", "label": "Task", "width": 180 }, + { "id": "substrate", "lane": "forgekit", "col": 1, "type": "security", "label": "Pre-action substrate", "width": 180 }, + { "id": "memory", "lane": "forgekit", "col": 4, "type": "database", "label": "Memory", "sublabel": "evidence-weighted", "width": 180 }, + { "id": "agent", "lane": "work-lane", "col": 2, "type": "backend", "label": "Agent acts", "sublabel": "external coding agent", "width": 180 }, + { "id": "outcome", "lane": "work-lane", "col": 3, "type": "external", "label": "Tests, CI or human", "width": 180 } + ], + "edges": [ + { "id": "task-check", "from": "task", "to": "substrate" }, + { "id": "missing-info", "from": "substrate", "to": "clarify", "label": "missing information", "role": "branch" }, + { "id": "clarify-back", "from": "clarify", "to": "task", "role": "return" }, + { "id": "enough-info", "from": "substrate", "to": "agent", "label": "enough information", "variant": "emphasis" }, + { "id": "agent-outcome", "from": "agent", "to": "outcome" }, + { "id": "outcome-memory", "from": "outcome", "to": "memory", "toSide": "bottom" }, + { "id": "memory-feeds", "from": "memory", "to": "substrate", "label": "feeds the next check", "variant": "dashed", "role": "return", "route": "straight" } + ] +} diff --git a/docs/diagrams/src/hook-sequence.sequence.json b/docs/diagrams/src/hook-sequence.sequence.json new file mode 100644 index 00000000..b16393ad --- /dev/null +++ b/docs/diagrams/src/hook-sequence.sequence.json @@ -0,0 +1,52 @@ +{ + "schema_version": 1, + "diagram_type": "sequence", + "meta": { + "title": "One Claude Code session with forgekit's hooks", + "locale": "en", + "quality_profile": "showcase", + "column_fit": "spread", + "viewBox": [1500, 696] + }, + "participants": [ + { "id": "developer", "type": "external", "label": "Developer" }, + { "id": "agent", "type": "frontend", "label": "Claude Code (agent)" }, + { "id": "hooks", "type": "security", "label": "forgekit hooks" }, + { "id": "state", "type": "database", "label": ".forge/ state" } + ], + "segments": [ + { "from": 156, "to": 214, "label": "Session start" }, + { "from": 228, "to": 358, "label": "Prompt" }, + { "from": 372, "to": 502, "label": "Tool call" }, + { "from": 516, "to": 610, "label": "Stop" } + ], + "messages": [ + { "id": "session-start", "from": "agent", "to": "hooks", "y": 168, "label": "SessionStart: recall-load · cortex session-start" }, + { "id": "inject-context", "from": "hooks", "to": "agent", "y": 204, "label": "inject learned lessons + last handoff (.forge/state.md)", "variant": "return" }, + { "id": "prompt", "from": "developer", "to": "agent", "y": 240, "label": "prompt", "variant": "emphasis" }, + { "id": "prompt-submit", "from": "agent", "to": "hooks", "y": 276, "label": "UserPromptSubmit: cortex prompt + preflight" }, + { "id": "cached-atlas", "from": "hooks", "to": "state", "y": 312, "label": "reads CACHED atlas only; never builds it in a hook" }, + { "id": "advisory", "from": "hooks", "to": "agent", "y": 348, "label": "substrate advisory: suggested model · impact · verify checklist", "variant": "return" }, + { "id": "pre-tool", "from": "agent", "to": "hooks", "y": 384, "label": "PreToolUse: protect-paths · cost-budget · doom-loop · cortex pre-edit", "variant": "security" }, + { "id": "allow-deny", "from": "hooks", "to": "agent", "y": 420, "label": "allow | deny", "variant": "return" }, + { "id": "post-tool", "from": "agent", "to": "hooks", "y": 456, "label": "PostToolUse: format-on-edit · secret-redact · cortex capture" }, + { "id": "capture", "from": "hooks", "to": "state", "y": 492, "label": "cortex capture: signals + evidence" }, + { "id": "stop", "from": "agent", "to": "hooks", "y": 528, "label": "Stop: completion-gate · lean-guard · session-learner · cortex stop" }, + { "id": "distill", "from": "hooks", "to": "state", "y": 564, "label": "cortex stop: distill lessons into the ledger" }, + { "id": "stop-result", "from": "hooks", "to": "agent", "y": 600, "label": "session ends | agent told what is missing", "variant": "return" } + ], + "activations": [ + { "participant": "agent", "from": 163, "to": 605, "type": "frontend" }, + { "participant": "hooks", "from": 163, "to": 209, "type": "security" }, + { "participant": "hooks", "from": 271, "to": 353, "type": "security" }, + { "participant": "hooks", "from": 379, "to": 425, "type": "security" }, + { "participant": "hooks", "from": 451, "to": 497, "type": "security" }, + { "participant": "hooks", "from": 523, "to": 605, "type": "security" } + ], + "cards": [ + { "dot": "amber", "title": "Advisory by default", "items": ["UserPromptSubmit output is advisory by default", "PreToolUse (Edit/Write/Bash) denies secret paths and dangerous writes; the others are advisory unless enforced"] }, + { "dot": "rose", "title": "Completion gate", "items": ["Sessions that touched code need a test in the diff or a fresh passing forge verify"] }, + { "dot": "slate", "title": "secret-redact", "items": ["Scrubs secrets from tool output after the tool runs"] }, + { "dot": "emerald", "title": ".forge/ state", "items": ["ledger · atlas cache · metrics"] } + ] +} diff --git a/docs/diagrams/src/ledger-flow.dataflow.json b/docs/diagrams/src/ledger-flow.dataflow.json new file mode 100644 index 00000000..6cf4241d --- /dev/null +++ b/docs/diagrams/src/ledger-flow.dataflow.json @@ -0,0 +1,42 @@ +{ + "schema_version": 1, + "diagram_type": "dataflow", + "meta": { + "title": "Proof-carrying memory: how claims gain and lose trust", + "locale": "en", + "quality_profile": "showcase", + "viewBox": [1080, 640], + "views": [ + {"id": "claims-in", "label": "Claims and evidence in", "focus": ["events", "oracles", "ledger"], "note": "Local events write content-addressed claims; independent oracles append evidence and move confidence."}, + {"id": "team-sync", "label": "Team sync over git", "focus": ["ledger", "teammates"], "note": "Ledgers exchange both ways via git union-merge, conflict-free."}, + {"id": "read-path", "label": "Merged read view", "focus": ["ledger", "readview", "recall", "lessons", "brain"], "note": "The merged read view feeds the recall list, lesson injection and the brain index."} + ] + }, + "stages": [ + {"label": "This machine · sources"}, + {"label": "This machine · ledger"}, + {"label": "This machine · read"}, + {"label": "This machine · feeds"}, + {"label": "Team · git"} + ], + "nodes": [ + {"id": "oracles", "type": "external", "label": "Independent oracles", "sublabel": "tests · CI", "tag": "human accept / revert", "stage": 0, "row": 0, "width": 150}, + {"id": "events", "type": "frontend", "label": "Local events", "sublabel": "recall / remember · cortex lesson", "tag": "reuse mint · diagnose", "stage": 0, "row": 1, "width": 150}, + {"id": "ledger", "type": "database", "label": ".forge/ledger/", "sublabel": "content-addressed claims", "tag": "append-only evidence logs", "stage": 1, "row": 1}, + {"id": "readview", "type": "backend", "label": "Merged read view", "stage": 2, "row": 1}, + {"id": "recall", "type": "frontend", "label": "Recall list", "stage": 3, "row": 0}, + {"id": "lessons", "type": "frontend", "label": "Lesson injection", "stage": 3, "row": 1}, + {"id": "brain", "type": "frontend", "label": "Brain index", "stage": 3, "row": 2}, + {"id": "teammates", "type": "database", "label": "Teammate ledgers", "stage": 4, "row": 3} + ], + "flows": [ + {"id": "write-claims", "from": "events", "to": "ledger", "label": "writes claims", "variant": "emphasis"}, + {"id": "append-evidence", "from": "oracles", "to": "ledger", "label": "append evidence", "classification": "moves confidence", "toSide": "top", "via": [[315, 157]]}, + {"id": "read-merged", "from": "ledger", "to": "readview", "label": "claims + evidence", "variant": "emphasis"}, + {"id": "feed-recall", "from": "readview", "to": "recall", "label": "feeds", "fromSide": "top", "via": [[530, 157]]}, + {"id": "feed-lessons", "from": "readview", "to": "lessons", "label": "feeds", "variant": "emphasis"}, + {"id": "feed-brain", "from": "readview", "to": "brain", "label": "feeds", "fromSide": "bottom", "via": [[530, 385]]}, + {"id": "sync-out", "from": "ledger", "to": "teammates", "label": "git union-merge", "classification": "conflict-free", "fromSide": "bottom", "toSide": "left", "via": [[315, 499]], "labelDy": -20}, + {"id": "sync-in", "from": "teammates", "to": "ledger", "label": "forge ledger merge / sync", "fromSide": "left", "toSide": "bottom", "via": [[315, 499]], "labelAt": [610, 519]} + ] +} diff --git a/docs/diagrams/src/llm-reconcile.workflow.json b/docs/diagrams/src/llm-reconcile.workflow.json new file mode 100644 index 00000000..472ce0e1 --- /dev/null +++ b/docs/diagrams/src/llm-reconcile.workflow.json @@ -0,0 +1,45 @@ +{ + "schema_version": 2, + "diagram_type": "workflow", + "meta": { + "title": "Rubric first, LLM second: how a proposal can move a verdict", + "locale": "en", + "quality_profile": "showcase" + }, + "lanes": [ + { "id": "rubric-lane", "label": "Rubric first" }, + { "id": "reconcile-lane", "label": "Reconcile" }, + { "id": "llm-lane", "label": "LLM second" } + ], + "semanticChecks": { + "allowedRoots": ["input"], + "allowedTerminals": ["moves", "holds"], + "requiredEdges": [ + { "from": "input", "to": "rubric" }, + { "from": "input", "to": "proposer" }, + { "from": "proposer", "to": "verify" }, + { "from": "verify", "to": "reconcile" }, + { "from": "rubric", "to": "reconcile" }, + { "from": "reconcile", "to": "moves" }, + { "from": "reconcile", "to": "holds" } + ] + }, + "nodes": [ + { "id": "input", "lane": "reconcile-lane", "col": 0, "type": "database", "label": "Task or edit", "width": 180 }, + { "id": "rubric", "lane": "rubric-lane", "col": 1, "type": "security", "label": "Deterministic rubric", "width": 180 }, + { "id": "proposer", "lane": "llm-lane", "col": 1, "type": "cloud", "label": "LLM proposer", "sublabel": "opt-in: FORGE_LLM=1", "tag": "never judges alone", "width": 180 }, + { "id": "verify", "lane": "llm-lane", "col": 2, "type": "security", "label": "Verify", "sublabel": "rubric band · repo grounding (grep) · tests", "width": 220 }, + { "id": "reconcile", "lane": "reconcile-lane", "col": 3, "type": "security", "label": "Reconcile", "width": 180 }, + { "id": "holds", "lane": "rubric-lane", "col": 4, "type": "security", "label": "Verdict holds", "sublabel": "deterministic", "width": 220 }, + { "id": "moves", "lane": "reconcile-lane", "col": 4, "type": "security", "label": "Verdict moves", "sublabel": "llm-cleared / lowered / raised / verified", "width": 220 } + ], + "edges": [ + { "id": "to-rubric", "from": "input", "to": "rubric", "fromSide": "top" }, + { "id": "to-proposer", "from": "input", "to": "proposer", "variant": "dashed", "fromSide": "bottom" }, + { "id": "rubric-reconcile", "from": "rubric", "to": "reconcile", "fromSide": "right", "toSide": "left" }, + { "id": "propose-verify", "from": "proposer", "to": "verify", "variant": "dashed" }, + { "id": "verify-reconcile", "from": "verify", "to": "reconcile", "variant": "dashed" }, + { "id": "passes", "from": "reconcile", "to": "moves", "label": "passes checks", "variant": "emphasis" }, + { "id": "fails", "from": "reconcile", "to": "holds", "label": "fails or unavailable" } + ] +} diff --git a/docs/diagrams/src/onboarding-loop.workflow.json b/docs/diagrams/src/onboarding-loop.workflow.json new file mode 100644 index 00000000..3b282523 --- /dev/null +++ b/docs/diagrams/src/onboarding-loop.workflow.json @@ -0,0 +1,36 @@ +{ + "schema_version": 2, + "diagram_type": "workflow", + "meta": { + "title": "Guided onboarding: what happens after forge init", + "locale": "en", + "quality_profile": "showcase" + }, + "lanes": [ + { "id": "setup", "label": "Setup" }, + { "id": "each-task", "label": "Each task" }, + { "id": "learning", "label": "Learning" } + ], + "mainPath": ["init", "configured", "work", "check", "edit", "learn"], + "semanticChecks": { + "allowedRoots": ["init"], + "allowedTerminals": [], + "requiredEdges": [{ "from": "learn", "to": "work" }] + }, + "nodes": [ + { "id": "init", "lane": "setup", "col": 0, "type": "messagebus", "label": "forge init", "width": 180, "height": 76 }, + { "id": "configured", "lane": "setup", "col": 1, "type": "database", "label": "Your tools configured", "sublabel": "from one source", "width": 180, "height": 76 }, + { "id": "work", "lane": "each-task", "col": 1, "type": "frontend", "label": "You work as usual", "width": 180, "height": 76 }, + { "id": "check", "lane": "each-task", "col": 2, "type": "security", "label": "Substrate checks", "sublabel": "ask first? which model? what breaks?", "width": 180, "height": 76 }, + { "id": "edit", "lane": "each-task", "col": 3, "type": "backend", "label": "Agent edits", "sublabel": "with guardrails", "width": 180, "height": 76 }, + { "id": "learn", "lane": "learning", "col": 3, "type": "database", "label": "Cortex learns", "sublabel": "from corrections", "width": 180, "height": 76 } + ], + "edges": [ + { "id": "init-configure", "from": "init", "to": "configured" }, + { "id": "configured-work", "from": "configured", "to": "work" }, + { "id": "work-check", "from": "work", "to": "check" }, + { "id": "check-edit", "from": "check", "to": "edit" }, + { "id": "edit-learn", "from": "edit", "to": "learn" }, + { "id": "next-task", "from": "learn", "to": "work", "label": "next task is smarter", "variant": "dashed", "role": "return" } + ] +} diff --git a/docs/diagrams/src/phase-map.architecture.json b/docs/diagrams/src/phase-map.architecture.json new file mode 100644 index 00000000..23514348 --- /dev/null +++ b/docs/diagrams/src/phase-map.architecture.json @@ -0,0 +1,38 @@ +{ + "schema_version": 1, + "diagram_type": "architecture", + "meta": { + "title": "Substrate v2 plan: phases and dependencies", + "locale": "en", + "quality_profile": "showcase" + }, + "components": [ + { "id": "p0", "type": "backend", "label": "P0 specs", "sublabel": "done", "pos": [40, 320], "size": [200, 70] }, + { "id": "p1", "type": "backend", "label": "P1 ledger core", "sublabel": "done", "pos": [300, 320], "size": [200, 70] }, + { "id": "p2", "type": "backend", "label": "P2 team sync", "sublabel": "done", "pos": [560, 60], "size": [200, 70] }, + { "id": "p6", "type": "backend", "label": "P6 UI quality gate", "sublabel": "done", "pos": [560, 190], "size": [200, 70] }, + { "id": "p4", "type": "backend", "label": "P4 context assembly", "sublabel": "partial", "pos": [560, 320], "size": [200, 70] }, + { "id": "p3", "type": "backend", "label": "P3 reuse cache", "sublabel": "done", "pos": [560, 450], "size": [200, 70] }, + { "id": "p5", "type": "backend", "label": "P5 loop closure", "sublabel": "done", "pos": [820, 320], "size": [200, 70] }, + { "id": "p7", "type": "backend", "label": "P7 dashboard", "sublabel": "done", "pos": [1080, 125], "size": [200, 70] }, + { "id": "p8", "type": "backend", "label": "P8 evaluation", "sublabel": "partial", "pos": [1080, 385], "size": [200, 70] } + ], + "connections": [ + { "id": "p0-p1", "from": "p0", "to": "p1" }, + { "id": "p1-p2", "from": "p1", "to": "p2" }, + { "id": "p1-p3", "from": "p1", "to": "p3" }, + { "id": "p1-p4", "from": "p1", "to": "p4", "variant": "dashed" }, + { "id": "p1-p6", "from": "p1", "to": "p6" }, + { "id": "p4-p5", "from": "p4", "to": "p5", "variant": "dashed" }, + { "id": "p2-p7", "from": "p2", "to": "p7" }, + { "id": "p5-p7", "from": "p5", "to": "p7" }, + { "id": "p6-p7", "from": "p6", "to": "p7" }, + { "id": "p3-p8", "from": "p3", "to": "p8", "variant": "dashed" }, + { "id": "p5-p8", "from": "p5", "to": "p8", "variant": "dashed" } + ], + "cards": [ + { "dot": "emerald", "title": "Done", "items": ["P0 · P1 · P2 · P3 · P5 · P6 · P7"] }, + { "dot": "amber", "title": "Partial", "items": ["P4 context assembly", "P8 evaluation", "Dashed links touch a partial phase"] }, + { "dot": "slate", "title": "Reading", "items": ["Each arrow points to the phase that depends on it"] } + ] +} diff --git a/docs/diagrams/src/pre-action-gate.workflow.json b/docs/diagrams/src/pre-action-gate.workflow.json new file mode 100644 index 00000000..4906930b --- /dev/null +++ b/docs/diagrams/src/pre-action-gate.workflow.json @@ -0,0 +1,59 @@ +{ + "schema_version": 2, + "diagram_type": "workflow", + "meta": { + "title": "The pre-action gate: one deterministic pass before the agent edits", + "locale": "en", + "quality_profile": "showcase" + }, + "lanes": [ + { "id": "intake", "label": "Intake" }, + { "id": "analysis", "label": "Analysis" }, + { "id": "safety", "label": "Safety + fit" } + ], + "semanticChecks": { + "allowedRoots": ["entities"], + "allowedTerminals": ["verdict"], + "requiredPaths": [{ "from": "entities", "to": "verdict" }] + }, + "nodes": [ + { "id": "entities", "lane": "intake", "col": 0, "type": "database", "label": "Referenced entities", "sublabel": "from the task", "width": 140 }, + { "id": "preflight", "lane": "intake", "col": 1, "type": "security", "label": "preflight", "sublabel": "assumption gap", "width": 140 }, + { "id": "route", "lane": "intake", "col": 2, "type": "security", "label": "route", "sublabel": "cheapest capable tier", "width": 140 }, + { "id": "atlas", "lane": "analysis", "col": 2, "type": "database", "label": "atlas", "sublabel": "code graph", "width": 140 }, + { "id": "impact", "lane": "analysis", "col": 3, "type": "database", "label": "impact", "sublabel": "blast radius", "width": 140 }, + { "id": "predict", "lane": "analysis", "col": 4, "type": "database", "label": "predict", "sublabel": "likely failing tests", "width": 140 }, + { "id": "reuse", "lane": "analysis", "col": 5, "type": "database", "label": "reuse", "sublabel": "cache hit?", "width": 140 }, + { "id": "context", "lane": "safety", "col": 5, "type": "security", "label": "context", "sublabel": "completeness gate", "width": 140 }, + { "id": "scope", "lane": "safety", "col": 4, "type": "database", "label": "scope", "sublabel": "coupled files", "width": 140 }, + { "id": "memory", "lane": "safety", "col": 3, "type": "database", "label": "memory", "sublabel": "recall + lessons", "width": 140 }, + { "id": "minimality", "lane": "safety", "col": 2, "type": "security", "label": "minimality", "sublabel": "lean footprint", "width": 140 }, + { "id": "anchor", "lane": "safety", "col": 1, "type": "security", "label": "goal-anchor", "sublabel": "drift check", "width": 140 }, + { "id": "verdict", "lane": "safety", "col": 0, "type": "security", "label": "Verdict", "sublabel": "advisory by default", "width": 140 } + ], + "edges": [ + { "id": "enter-gate", "from": "entities", "to": "preflight" }, + { "id": "preflight-route", "from": "preflight", "to": "route" }, + { "id": "route-atlas", "from": "route", "to": "atlas" }, + { "id": "atlas-impact", "from": "atlas", "to": "impact" }, + { "id": "impact-predict", "from": "impact", "to": "predict" }, + { "id": "predict-reuse", "from": "predict", "to": "reuse" }, + { "id": "reuse-context", "from": "reuse", "to": "context" }, + { "id": "context-scope", "from": "context", "to": "scope" }, + { "id": "scope-memory", "from": "scope", "to": "memory" }, + { "id": "memory-minimality", "from": "memory", "to": "minimality" }, + { "id": "minimality-anchor", "from": "minimality", "to": "anchor" }, + { "id": "anchor-verdict", "from": "anchor", "to": "verdict", "variant": "emphasis" } + ], + "cards": [ + { + "dot": "amber", + "title": "FORGE_ENFORCE=1 blocks only the strongest signals", + "items": [ + "A vacuous task", + "Required context that cannot be assembled", + "A large impact set from a fresh graph" + ] + } + ] +} diff --git a/docs/diagrams/src/reuse-cache.workflow.json b/docs/diagrams/src/reuse-cache.workflow.json new file mode 100644 index 00000000..e7239cf9 --- /dev/null +++ b/docs/diagrams/src/reuse-cache.workflow.json @@ -0,0 +1,44 @@ +{ + "schema_version": 2, + "diagram_type": "workflow", + "meta": { + "title": "Reuse cache: serve only while the proof holds", + "locale": "en", + "quality_profile": "showcase", + "views": [ + {"id": "serve-path", "label": "Serve path", "focus": ["spec", "exact", "near", "guard", "check", "served"], "note": "A hit is served only while its proof holds: confidence ≥ 0.6, same sha256, unchanged dependency contracts."}, + {"id": "miss-path", "label": "Miss path", "focus": ["adapt", "check", "generate", "verify", "mint"], "note": "A miss or failed revalidation is generated, verified and minted into .forge/ledger/ for next time."} + ] + }, + "lanes": [ + {"id": "lookup", "label": "Lookup ladder"}, + {"id": "gate", "label": "Serve gate"}, + {"id": "miss", "label": "Miss path", "variant": "exception"} + ], + "mainPath": ["spec", "exact", "check", "served"], + "nodes": [ + {"id": "spec", "lane": "lookup", "col": 0, "type": "external", "label": "Spec", "sublabel": "the task text", "width": 150, "height": 76}, + {"id": "exact", "lane": "lookup", "col": 1, "type": "messagebus", "label": "Exact tier", "sublabel": "lossless except whitespace", "tag": "Unicode-normalized; case kept", "width": 150, "height": 92}, + {"id": "near", "lane": "lookup", "col": 2, "type": "messagebus", "label": "Near tier", "sublabel": "MinHash + LSH similarity", "width": 150, "height": 76}, + {"id": "guard", "lane": "lookup", "col": 3, "type": "security", "label": "Semantic guard", "sublabel": "operators · numbers · literals", "tag": "identifiers · paths · negation", "width": 150, "height": 92}, + {"id": "adapt", "lane": "lookup", "col": 4, "type": "messagebus", "label": "Adapt tier", "sublabel": "a match that differs", "width": 150, "height": 76}, + {"id": "check", "lane": "gate", "col": 2, "type": "security", "label": "Proof check", "sublabel": "serve floor: confidence ≥ 0.6", "tag": "same sha256 · dep contracts hold", "width": 150, "height": 92}, + {"id": "served", "lane": "gate", "col": 3, "type": "messagebus", "label": "Served", "sublabel": "proof holds · adds no evidence", "tag": "no atlas → requiresRevalidation", "width": 150, "height": 92}, + {"id": "generate", "lane": "miss", "col": 3, "type": "backend", "label": "Generate", "width": 150, "height": 60}, + {"id": "verify", "lane": "miss", "col": 2, "type": "security", "label": "Verify", "width": 150, "height": 60}, + {"id": "mint", "lane": "miss", "col": 1, "type": "database", "label": "forge reuse mint", "sublabel": "claim → .forge/ledger/", "tag": "available next time", "width": 150, "height": 92} + ], + "edges": [ + {"id": "lookup-exact", "from": "spec", "to": "exact", "label": "lookup", "variant": "emphasis", "role": "main"}, + {"id": "exact-hit", "from": "exact", "to": "check", "label": "hit", "variant": "emphasis", "role": "main", "fromSide": "bottom", "toSide": "left"}, + {"id": "check-served", "from": "check", "to": "served", "label": "all hold", "variant": "emphasis", "role": "main", "fromSide": "right", "toSide": "left"}, + {"id": "exact-near", "from": "exact", "to": "near", "label": "otherwise", "role": "branch"}, + {"id": "near-guard", "from": "near", "to": "guard", "label": "similar", "role": "branch", "fromSide": "right", "toSide": "left"}, + {"id": "guard-hit", "from": "guard", "to": "check", "label": "agrees: hit", "role": "branch", "fromSide": "bottom", "toSide": "top"}, + {"id": "guard-adapt", "from": "guard", "to": "adapt", "label": "differs", "role": "branch"}, + {"id": "adapt-miss", "from": "adapt", "to": "generate", "label": "otherwise: miss", "variant": "dashed", "role": "error", "fromSide": "bottom", "toSide": "right"}, + {"id": "check-fails", "from": "check", "to": "generate", "label": "fails", "variant": "security", "role": "error", "fromSide": "bottom", "toSide": "top", "route": "drop"}, + {"id": "generate-verify", "from": "generate", "to": "verify"}, + {"id": "verify-mint", "from": "verify", "to": "mint"} + ] +} diff --git a/docs/diagrams/src/router-cascade.workflow.json b/docs/diagrams/src/router-cascade.workflow.json new file mode 100644 index 00000000..80ce015a --- /dev/null +++ b/docs/diagrams/src/router-cascade.workflow.json @@ -0,0 +1,52 @@ +{ + "schema_version": 2, + "diagram_type": "workflow", + "meta": { + "title": "forge route universal: a cascade under an explicit objective", + "locale": "en", + "quality_profile": "showcase", + "views": [ + { "id": "choose", "label": "Choose a cascade", "focus": ["route-cmd", "estimates", "choose", "recommend", "infeasible"], "note": "Task features and per-model fits select the cheapest cascade that meets the objective, or INFEASIBLE." }, + { "id": "run", "label": "Run and escalate", "focus": ["recommend", "attempt", "check", "escalate", "done"], "note": "Each attempt is checked; a failure escalates to the next model in the cascade." }, + { "id": "learn", "label": "Record and fit", "focus": ["check", "outcome", "fit"], "note": "forge route outcome records each outcome; forge route fit refits a local point estimate, shrunk toward the shipped prior." } + ] + }, + "lanes": [ + { "id": "fallbacks", "label": "Fallbacks", "variant": "exception" }, + { "id": "route", "label": "Route (advice)" }, + { "id": "run", "label": "Run the cascade" }, + { "id": "learn", "label": "Record + fit" } + ], + "mainPath": ["route-cmd", "estimates", "choose", "recommend", "attempt", "check", "done"], + "nodes": [ + { "id": "route-cmd", "lane": "route", "col": 0, "type": "frontend", "label": "forge route universal", "sublabel": "\"\" → 12 task features", "width": 156 }, + { "id": "estimates", "lane": "route", "col": 1, "type": "backend", "label": "Per model", "sublabel": "P(solve) + expected cost", "width": 132 }, + { "id": "choose", "lane": "route", "col": 2, "type": "backend", "label": "Cheapest cascade", "sublabel": "that meets the objective", "width": 132 }, + { "id": "recommend", "lane": "route", "col": 3, "type": "backend", "label": "Recommendation", "sublabel": "advice only if no provider id", "width": 150 }, + { "id": "infeasible", "lane": "fallbacks", "col": 3, "type": "backend", "label": "INFEASIBLE · exit 1", "sublabel": "labeled least-bad fallback", "width": 150 }, + { "id": "escalate", "lane": "fallbacks", "col": 4, "type": "external", "label": "Escalate", "sublabel": "next model in the cascade", "width": 168 }, + { "id": "attempt", "lane": "run", "col": 3, "type": "external", "label": "Attempt 1", "sublabel": "first model in cascade", "width": 150 }, + { "id": "check", "lane": "run", "col": 4, "type": "backend", "label": "Check", "sublabel": "tests / forge verify", "width": 168 }, + { "id": "done", "lane": "run", "col": 5, "type": "backend", "label": "Done", "width": 160 }, + { "id": "outcome", "lane": "learn", "col": 4, "type": "backend", "label": "forge route outcome", "sublabel": "self-reported unless --verify-run", "width": 168 }, + { "id": "fit", "lane": "learn", "col": 5, "type": "backend", "label": "forge route fit", "sublabel": "MAP shrinkage to shipped prior", "width": 160 } + ], + "edges": [ + { "id": "cmd-estimates", "from": "route-cmd", "to": "estimates" }, + { "id": "estimates-choose", "from": "estimates", "to": "choose" }, + { "id": "choose-feasible", "from": "choose", "to": "recommend", "label": "feasible" }, + { "id": "choose-infeasible", "from": "choose", "to": "infeasible", "label": "infeasible", "role": "error", "variant": "dashed" }, + { "id": "recommend-attempt", "from": "recommend", "to": "attempt" }, + { "id": "attempt-check", "from": "attempt", "to": "check" }, + { "id": "check-pass", "from": "check", "to": "done", "label": "pass", "variant": "emphasis" }, + { "id": "check-fail", "from": "check", "to": "escalate", "label": "fail", "role": "error", "variant": "dashed" }, + { "id": "escalate-check", "from": "escalate", "to": "check", "role": "return", "variant": "dashed" }, + { "id": "record-outcome", "from": "check", "to": "outcome", "label": "each outcome", "role": "branch" }, + { "id": "outcome-fit", "from": "outcome", "to": "fit", "role": "branch" } + ], + "cards": [ + { "dot": "cyan", "title": "Per-model estimates", "items": ["P(solve): multidimensional IRT fit (correlated failures)", "Expected cost of one attempt: the cost fit"] }, + { "dot": "violet", "title": "Objective", "items": ["match-best-single | target:p | value:$ | budget:$", "INFEASIBLE reports the minimum achievable expected cost"] }, + { "dot": "amber", "title": "Caveats", "items": ["Expected cost is not a cap: the worst case runs every attempt", "Outcomes are self-reported unless --verify-run ties them to a verify event"] } + ] +} diff --git a/docs/diagrams/src/system.architecture.json b/docs/diagrams/src/system.architecture.json new file mode 100644 index 00000000..52cac193 --- /dev/null +++ b/docs/diagrams/src/system.architecture.json @@ -0,0 +1,39 @@ +{ + "schema_version": 1, + "diagram_type": "architecture", + "meta": { + "title": "forgekit: one source, native configs, enforced guards, evidence-carrying memory", + "locale": "en", + "quality_profile": "showcase", + "views": [ + { "id": "one-source", "label": "One source, native configs", "focus": ["source", "forge-sync", "native-configs"], "note": "source/ is the one source of truth; forge sync emits each tool's native config." }, + { "id": "four-layers", "label": "The four layers", "focus": ["source", "tools", "crew", "guards", "mcp"], "note": "source/ configures tools, crew, guards and mcp; guards are deterministic, enforced hooks." }, + { "id": "memory", "label": "Evidence-carrying memory", "focus": ["local-events", "oracles", "ledger", "teammates"], "note": "Local events add content-addressed claims; oracles move confidence; ledgers git union-merge with teammates." } + ] + }, + "components": [ + { "id": "source", "type": "database", "label": "source/ · the one source of truth", "sublabel": "rules.json · substrate.json · mcp.json", "pos": [40, 60], "size": [270, 76] }, + { "id": "forge-sync", "type": "backend", "label": "forge sync", "sublabel": "content hash · DO-NOT-EDIT headers", "pos": [470, 60], "size": [250, 76] }, + { "id": "native-configs", "type": "external", "label": "Native configs for ten tools", "sublabel": "CLAUDE.md · AGENTS.md · .cursor · .gemini · .aider · …", "pos": [860, 60], "size": [340, 76] }, + { "id": "tools", "type": "backend", "label": "tools", "sublabel": "model-invoked skills", "pos": [40, 260], "size": [230, 70] }, + { "id": "crew", "type": "backend", "label": "crew", "sublabel": "isolated sub-agents", "pos": [350, 260], "size": [230, 70] }, + { "id": "guards", "type": "security", "label": "guards · enforced", "sublabel": "deterministic hooks", "pos": [660, 260], "size": [230, 70] }, + { "id": "mcp", "type": "backend", "label": "mcp", "sublabel": "atlas + substrate MCP server", "pos": [970, 260], "size": [230, 70] }, + { "id": "local-events", "type": "messagebus", "label": "Local events", "sublabel": "cortex · recall · reuse · diagnose", "pos": [40, 470], "size": [260, 76] }, + { "id": "ledger", "type": "database", "label": "PCM ledger", "sublabel": ".forge/ledger/", "pos": [520, 470], "size": [240, 76] }, + { "id": "teammates", "type": "external", "label": "Teammate ledgers", "pos": [980, 470], "size": [220, 76] }, + { "id": "oracles", "type": "external", "label": "Independent oracles", "sublabel": "tests · CI · human accept/revert", "pos": [510, 660], "size": [260, 76] } + ], + "boundaries": [ + { "kind": "region", "label": "the four layers", "wraps": ["tools", "crew", "guards", "mcp"] } + ], + "connections": [ + { "id": "source-to-sync", "from": "source", "to": "forge-sync" }, + { "id": "sync-emits-configs", "from": "forge-sync", "to": "native-configs", "label": "emits" }, + { "id": "source-configures-layers", "from": "source", "to": "tools", "label": "configures all four", "variant": "dashed", "fromSide": "bottom", "toSide": "top" }, + { "id": "events-claims", "from": "local-events", "to": "ledger", "label": "content-addressed claims" }, + { "id": "oracles-confidence", "from": "oracles", "to": "ledger", "label": "move confidence" }, + { "id": "ledger-to-teammates", "from": "ledger", "to": "teammates", "label": "git union-merge, conflict-free" }, + { "id": "teammates-to-ledger", "from": "teammates", "to": "ledger" } + ] +} diff --git a/docs/diagrams/src/team-loop.workflow.json b/docs/diagrams/src/team-loop.workflow.json new file mode 100644 index 00000000..cb52a53d --- /dev/null +++ b/docs/diagrams/src/team-loop.workflow.json @@ -0,0 +1,30 @@ +{ + "schema_version": 2, + "diagram_type": "workflow", + "meta": { + "title": "The team loop: work, verify, remember", + "locale": "en", + "quality_profile": "showcase" + }, + "lanes": [ + {"id": "work", "label": "Work"}, + {"id": "verify", "label": "Verify"}, + {"id": "remember", "label": "Remember"} + ], + "mainPath": ["precheck", "edit", "oracles", "ledger"], + "nodes": [ + {"id": "precheck", "lane": "work", "col": 0, "type": "security", "label": "Substrate pre-checks", "width": 150}, + {"id": "edit", "lane": "work", "col": 1, "type": "backend", "label": "Edit", "width": 150}, + {"id": "oracles", "lane": "verify", "col": 2, "type": "security", "label": "Oracles", "sublabel": "forge verify · forge imagine --run · CI · human accept/revert", "width": 280}, + {"id": "ledger", "lane": "remember", "col": 4, "type": "database", "label": "Team ledger", "sublabel": ".forge/ledger/", "width": 150}, + {"id": "teammates", "lane": "remember", "col": 5, "type": "database", "label": "Teammates' ledgers", "width": 150} + ], + "edges": [ + {"id": "precheck-edit", "from": "precheck", "to": "edit", "variant": "emphasis", "role": "main"}, + {"id": "edit-oracles", "from": "edit", "to": "oracles", "variant": "emphasis", "role": "main"}, + {"id": "outcomes", "from": "oracles", "to": "ledger", "label": "outcomes move claim val", "variant": "emphasis", "role": "main"}, + {"id": "share-out", "from": "ledger", "to": "teammates", "label": "git + forge ledger merge", "variant": "dashed", "role": "async"}, + {"id": "share-in", "from": "teammates", "to": "ledger", "label": "git + forge ledger merge", "variant": "dashed", "role": "async"}, + {"id": "feed-back", "from": "ledger", "to": "precheck", "label": "lessons · facts · reuse hits", "variant": "emphasis", "role": "return"} + ] +} diff --git a/docs/diagrams/src/verify-pipeline.workflow.json b/docs/diagrams/src/verify-pipeline.workflow.json new file mode 100644 index 00000000..305538ab --- /dev/null +++ b/docs/diagrams/src/verify-pipeline.workflow.json @@ -0,0 +1,60 @@ +{ + "schema_version": 2, + "diagram_type": "workflow", + "meta": { + "title": "forge verify: a PASS bound to the code that was tested", + "locale": "en", + "quality_profile": "showcase" + }, + "lanes": [ + { "id": "state", "label": "Code state" }, + { "id": "suites", "label": "Suites" }, + { "id": "files", "label": ".forge/" } + ], + "mainPath": ["plan", "pre", "run", "post", "aggregate", "symbols", "stamp"], + "semanticChecks": { + "allowedRoots": ["config"], + "allowedTerminals": ["events"] + }, + "nodes": [ + { "id": "config", "lane": "files", "col": 0, "type": "database", "label": ".forge/forge.config.json", "sublabel": "under verify", "tag": "workspaces · exclude · generated", "width": 180 }, + { "id": "plan", "lane": "suites", "col": 0, "type": "messagebus", "label": "Plan suites", "sublabel": "root + nested packages with a suite", "tag": "scripts.test · pytest config · go.mod · …", "width": 180 }, + { "id": "pre", "lane": "state", "col": 1, "type": "database", "label": "Pre-run state", "sublabel": "HEAD · diffs · untracked files", "tag": "untracked: path/mode/size/sha256", "width": 180 }, + { "id": "run", "lane": "suites", "col": 2, "type": "messagebus", "label": "Run each suite", "sublabel": "in its own directory", "width": 180 }, + { "id": "post", "lane": "state", "col": 3, "type": "database", "label": "Post-run state", "sublabel": "changed → INCOMPLETE (mutated)", "width": 180 }, + { "id": "aggregate", "lane": "suites", "col": 4, "type": "security", "label": "Aggregate", "sublabel": "PASS · FAIL · INCOMPLETE · NOT_CONFIGURED", "width": 200 }, + { "id": "symbols", "lane": "suites", "col": 5, "type": "security", "label": "Hallucinated-symbol check", "sublabel": "against the atlas", "width": 200 }, + { "id": "stamp", "lane": "files", "col": 5, "type": "database", "label": ".forge/provenance.json", "sublabel": "MAC-sealed stamp", "tag": "bound to the pre-run state", "width": 200 }, + { "id": "events", "lane": "files", "col": 4, "type": "database", "label": ".forge/verify-events.jsonl", "sublabel": "verifier event appended", "width": 200 } + ], + "edges": [ + { "id": "config-plan", "from": "config", "to": "plan", "variant": "dashed" }, + { "id": "plan-pre", "from": "plan", "to": "pre", "fromSide": "right", "toSide": "bottom" }, + { "id": "pre-run", "from": "pre", "to": "run", "fromSide": "right", "toSide": "top" }, + { "id": "run-post", "from": "run", "to": "post", "fromSide": "right", "toSide": "bottom" }, + { "id": "post-aggregate", "from": "post", "to": "aggregate", "fromSide": "right", "toSide": "top" }, + { "id": "aggregate-symbols", "from": "aggregate", "to": "symbols" }, + { "id": "symbols-stamp", "from": "symbols", "to": "stamp", "variant": "emphasis" }, + { "id": "stamp-events", "from": "stamp", "to": "events", "variant": "dashed" } + ], + "cards": [ + { + "dot": "emerald", + "title": "Aggregate", + "items": [ + "Any suite FAIL → FAIL", + "Any package without a verdict → INCOMPLETE", + "All covered and passing → PASS", + "No runner at all → NOT_CONFIGURED" + ] + }, + { + "dot": "cyan", + "title": "Coverage and mutation", + "items": [ + "A root script that runs every workspace covers them once", + "Interpreter caches and generated paths do not count as a change" + ] + } + ] +} diff --git a/docs/diagrams/system.svg b/docs/diagrams/system.svg new file mode 100644 index 00000000..45a5ca4f --- /dev/null +++ b/docs/diagrams/system.svg @@ -0,0 +1,639 @@ + + + forgekit: one source, native configs, enforced guards, evidence-carrying memory + An architecture diagram generated by Archify. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + source/ · the one source of truth · rules.json · substrate.json · mcp.json · Architecture component + + + + source/ · the one source of truth + rules.json · substrate.json · mcp.json + + + + forge sync · content hash · DO-NOT-EDIT headers · Architecture component + + + + forge sync + content hash · DO-NOT-EDIT headers + + + + Native configs for ten tools · CLAUDE.md · AGENTS.md · .cursor · .gemini · .aider · … · Architecture component + + + + Native configs for ten tools + CLAUDE.md · AGENTS.md · .cursor · .gemini · .aider · … + + + + tools · model-invoked skills · the four layers + + + + tools + model-invoked skills + + + + crew · isolated sub-agents · the four layers + + + + crew + isolated sub-agents + + + + guards · enforced · deterministic hooks · the four layers + + + + guards · enforced + deterministic hooks + + + + mcp · atlas + substrate MCP server · the four layers + + + + mcp + atlas + substrate MCP server + + + + Local events · cortex · recall · reuse · diagnose · Architecture component + + + + Local events + cortex · recall · reuse · diagnose + + + + PCM ledger · .forge/ledger/ · Architecture component + + + + PCM ledger + .forge/ledger/ + + + + Teammate ledgers · Architecture component + + + + Teammate ledgers + + + + Independent oracles · tests · CI · human accept/revert · Architecture component + + + + Independent oracles + tests · CI · human accept/revert + + + + + + + emits + + + + configures all four + + + + content-addressed claims + + + + move confidence + + + + git union-merge, conflict-free + + + + + + + the four layers + + + + + Legend + + + Backend + + + + Database + + + + Security + + + + Message bus + + + + External + + + \ No newline at end of file diff --git a/docs/diagrams/team-loop.svg b/docs/diagrams/team-loop.svg new file mode 100644 index 00000000..1210a6fc --- /dev/null +++ b/docs/diagrams/team-loop.svg @@ -0,0 +1,560 @@ + + + The team loop: work, verify, remember + A workflow diagram generated by Archify. + + + + + + + + + + + + + + + + + + + + + + + + + 01 / Work + + + 02 / Verify + + + 03 / Remember + + + + + + + + + + + + + + + + + + Substrate pre-checks · Work + + + + Substrate pre-checks + + + + Edit · Work + + + + Edit + + + + Oracles · forge verify · forge imagine --run · CI · human accept/revert · Verify + + + + Oracles + forge verify · forge imagine --run · CI · human accept/revert + + + + Team ledger · .forge/ledger/ · Remember + + + + Team ledger + .forge/ledger/ + + + + Teammates' ledgers · Remember + + + + Teammates' ledgers + + + + + + + lessons · facts · reuse hits + + + + outcomes move claim val + + + + + git + forge ledger merge + + + + git + forge ledger merge + + + + + Legend + + + Agent logic + + + + Policy + + + + Context / trace + + + \ No newline at end of file diff --git a/docs/diagrams/verify-pipeline.svg b/docs/diagrams/verify-pipeline.svg new file mode 100644 index 00000000..660f55a0 --- /dev/null +++ b/docs/diagrams/verify-pipeline.svg @@ -0,0 +1,612 @@ + + + forge verify: a PASS bound to the code that was tested + A workflow diagram generated by Archify. + + + + + + + + + + + + + + + + + + + + + + + + + 01 / Code state + + + 02 / Suites + + + 03 / .forge/ + + + + + + + + + + + + + + + + + + + + Pre-run state · HEAD · diffs · untracked files · Code state · untracked: path/mode/size/sha256 + + + + Pre-run state + HEAD · diffs · untracked files + untracked: path/mode/size/sha256 + + + + Post-run state · changed → INCOMPLETE (mutated) · Code state + + + + Post-run state + changed → INCOMPLETE (mutated) + + + + Plan suites · root + nested packages with a suite · Suites · scripts.test · pytest config · go.mod · … + + + + Plan suites + root + nested packages with a suite + scripts.test · pytest config · go.mod · … + + + + Run each suite · in its own directory · Suites + + + + Run each suite + in its own directory + + + + Aggregate · PASS · FAIL · INCOMPLETE · NOT_CONFIGURED · Suites + + + + Aggregate + PASS · FAIL · INCOMPLETE · NOT_CONFIGURED + + + + Hallucinated-symbol check · against the atlas · Suites + + + + Hallucinated-symbol check + against the atlas + + + + .forge/forge.config.json · under verify · .forge/ · workspaces · exclude · generated + + + + .forge/forge.config.json + under verify + workspaces · exclude · generated + + + + .forge/verify-events.jsonl · verifier event appended · .forge/ + + + + .forge/verify-events.jsonl + verifier event appended + + + + .forge/provenance.json · MAC-sealed stamp · .forge/ · bound to the pre-run state + + + + .forge/provenance.json + MAC-sealed stamp + bound to the pre-run state + + + + + + + + + + + + + + + Legend + + + Policy + + + + Tool action + + + + Context / trace + + + \ No newline at end of file diff --git a/docs/plans/substrate-v2/00-overview.md b/docs/plans/substrate-v2/00-overview.md index 4ae289ee..db47a37c 100644 --- a/docs/plans/substrate-v2/00-overview.md +++ b/docs/plans/substrate-v2/00-overview.md @@ -75,27 +75,11 @@ later phase stores its state as PCM claims. P0–P3 and P5–P7 have shipped (v0 are partial** — their acceptance criteria are not met (reasons in the table). _(Corrected 2026-09-26: this said "**All phases have shipped** (v0.5.0)", and every node below was green.)_ -Green nodes are shipped; amber nodes are partial. - -```mermaid -%%{init: {'theme':'base','themeVariables':{'primaryColor':'#201a15','primaryTextColor':'#f2ede7','primaryBorderColor':'#372c22','lineColor':'#f26430','secondaryColor':'#272019','tertiaryColor':'#171310','edgeLabelBackground':'#201a15','clusterBkg':'#171310','clusterBorder':'#4a3b2e','fontFamily':'ui-sans-serif, system-ui, sans-serif','fontSize':'14px'},'flowchart':{'curve':'basis','padding':10,'nodeSpacing':36,'rankSpacing':44}}}%% -flowchart LR - P0["P0 specs"] --> P1["P1 ledger core"] - P1 --> P2["P2 team sync"] - P1 --> P3["P3 reuse cache"] - P1 --> P4["P4 context assembly"] - P1 --> P6["P6 UI quality gate"] - P4 --> P5["P5 loop closure"] - P2 --> P7["P7 dashboard"] - P5 --> P7 - P6 --> P7 - P3 --> P8["P8 evaluation"] - P5 --> P8 - classDef done fill:#1f3d2b,stroke:#67e8a5,color:#f2ede7; - classDef partial fill:#3d321c,stroke:#e8b64a,color:#f2ede7; - class P0,P1,P2,P3,P5,P6,P7 done; - class P4,P8 partial; -``` +Each phase is labeled done or partial; a dashed arrow touches a partial phase. + +[![Substrate v2 phases: P0 specs leads to P1 ledger core, which feeds P2 team sync, P3 reuse cache, P4 context assembly and P6 UI quality gate; P4 leads to P5 loop closure; P2, P5 and P6 feed P7 dashboard; P3 and P5 feed P8 evaluation; P4 and P8 are partial, the rest are done](../../diagrams/phase-map.svg)](https://codewithjuber.github.io/forgekit/diagrams/phase-map.html) + +[Open the interactive diagram](https://codewithjuber.github.io/forgekit/diagrams/phase-map.html): pan, zoom, search, and trace any node. | Phase | Delivers | Depends on | Acceptance | | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------- | -------------------------------------------------------------------------------------------------------------------- | diff --git a/landing/404.html b/landing/404.html index 1eef4815..9091ceab 100644 --- a/landing/404.html +++ b/landing/404.html @@ -19,7 +19,7 @@ property="og:description" content="Shared memory, impact analysis, and guardrail hooks for AI coding agents — authored once, emitted as native config for Claude Code, Codex, Cursor, Gemini, Aider, and more." /> - + @@ -28,7 +28,7 @@ name="twitter:description" content="Shared memory, impact analysis, and guardrail hooks for AI coding agents — authored once, emitted as native config for Claude Code, Codex, Cursor, Gemini, Aider, and more." /> - + diff --git a/landing/index.html b/landing/index.html index 790aff98..c8765397 100644 --- a/landing/index.html +++ b/landing/index.html @@ -15,13 +15,13 @@ - + - + -
Open source cognitive substrateforgekit v1.6.0 · beta

One operating
memory. Every
coding agent.

ForgeKit gives every AI coding tool the same memory and foresight—with automatic guardrails on Claude Code—without locking your work inside one vendor or one chat window.

Runtime deps
0
Native targets
9
License
MIT
FK / PREFLIGHTSYSTEM READY
01
REQUESTRefactor authentication flow
00:118
  1. 01Memory recalledPASS
  2. 02Blast radius mappedPASS
  3. 03Guardrails checkedPASS
TRACE FK-031-7D4PROCEED →
01 / The substrateState before action

The missing layer between
your intent and your agent.

Models are capable. Their operating context is fragile. ForgeKit supplies the durable layer that travels with the repository and shows up before the next action.

ACTIVE CAPABILITY / 01

Context that survives the chat.

Forge keeps decisions, lessons, and project state in the repository—so Claude, Codex, Cursor, and the next agent all inherit the same working memory.

3 records recalled
TYPERECORDSTATE
decisionUse SQLite for local-first state94%
lessonRun schema checks before generation88%
preferenceKeep the CLI dependency-free82%
02 / The protocolOne request · five checks · one trace

Action should leave evidence.

Forge turns agent behavior into a reviewable sequence. Each meaningful move begins with context and ends with proof.

  1. 01Recall

    Load relevant decisions and lessons.

  2. 02Classify

    Measure scope, cost, and reversibility.

  3. 03Foresee

    Map downstream surfaces before editing.

  4. 04Gate

    Pause risky or under-specified actions.

  5. 05Trace

    Record what changed and how it was verified.

03 / One sourceTen native targets

Change the agent. Keep the operating system.

One source emits each tool’s native configuration. Your rules and memory stay with the project—not the provider.

  • 01Claude Code
  • 02Codex
  • 03Cursor
  • 04Gemini
  • 05Aider
  • 06Copilot
  • 07Windsurf
  • 08Zed
  • 09Continue
  • 10OpenClaw

Plus MCP configuration for Roo Code and VS Code-compatible clients.

04 / Evidence ledgerMeasured, not invented

Fast enough to stay in the loop.

ForgeKit publishes the measurements behind its claims. The numbers below come from repository benchmarks and evaluation reports—not a marketing dashboard.

Pre-action gate
851ms
End-to-end benchmark
Blast-radius scan
1.68ms
Heuristic analysis
Held-out routing cost
+20.2%
vs always-premium, 80 tasks
Runtime dependencies
0
Node.js standard library
Inspect the evidence
05 / Honest limitsProfessional, not magical

The guardrail is not the road.

ForgeKit improves agent judgment; it does not replace yours. The project labels its assumptions so you can decide where to trust, test, or intervene.

  • 01

    Claude Code is the deepest-tested integration. Other targets have less real-world exercise today.

  • 02

    Blast-radius analysis is heuristic. It guides review; it is not a formal dependency proof.

  • 03

    Guardrails are not a sandbox. Keep permissions, review, and backups appropriate to the work.

06 / Start hereAbout sixty seconds

Give the next agent a better starting point.

Install ForgeKit, run forge init in your repository, and keep one shared operating context across every tool.

Open the quickstart
forgekit / install
 /plugin marketplace add CodeWithJuber/forgekit
+
Open source cognitive substrateforgekit v1.6.0 · beta

One operating
memory. Every
coding agent.

ForgeKit gives every AI coding tool the same memory and foresight—with automatic guardrails on Claude Code—without locking your work inside one vendor or one chat window.

Runtime deps
0
Native targets
10
License
MIT
FK / PREFLIGHTSYSTEM READY
01
REQUESTRefactor authentication flow
00:118
  1. 01Memory recalledPASS
  2. 02Blast radius mappedPASS
  3. 03Guardrails checkedPASS
TRACE FK-031-7D4PROCEED →
01 / The substrateState before action

The missing layer between
your intent and your agent.

Models are capable. Their operating context is fragile. ForgeKit supplies the durable layer that travels with the repository and shows up before the next action.

ACTIVE CAPABILITY / 01

Context that survives the chat.

Forge keeps decisions, lessons, and project state in the repository—so Claude, Codex, Cursor, and the next agent all inherit the same working memory.

3 records recalled
TYPERECORDSTATE
decisionUse SQLite for local-first state94%
lessonRun schema checks before generation88%
preferenceKeep the CLI dependency-free82%
02 / The protocolOne request · five checks · one trace

Action should leave evidence.

Forge turns agent behavior into a reviewable sequence. Each meaningful move begins with context and ends with proof.

  1. 01Recall

    Load relevant decisions and lessons.

  2. 02Classify

    Measure scope, cost, and reversibility.

  3. 03Foresee

    Map downstream surfaces before editing.

  4. 04Gate

    Pause risky or under-specified actions.

  5. 05Trace

    Record what changed and how it was verified.

03 / One sourceTen native targets

Change the agent. Keep the operating system.

One source emits each tool’s native configuration. Your rules and memory stay with the project—not the provider.

  • 01Claude Code
  • 02Codex
  • 03Cursor
  • 04Gemini
  • 05Aider
  • 06Copilot
  • 07Windsurf
  • 08Zed
  • 09Continue
  • 10OpenClaw

Plus MCP configuration for Roo Code and VS Code-compatible clients.

04 / Evidence ledgerMeasured, not invented

Fast enough to stay in the loop.

ForgeKit publishes the measurements behind its claims. The numbers below come from repository benchmarks and evaluation reports—not a marketing dashboard.

Pre-action gate
851ms
End-to-end benchmark
Blast-radius scan
1.68ms
Heuristic analysis
Held-out routing cost
+20.2%
vs always-premium, 80 tasks
Runtime dependencies
0
Node.js standard library
Inspect the evidence
05 / Honest limitsProfessional, not magical

The guardrail is not the road.

ForgeKit improves agent judgment; it does not replace yours. The project labels its assumptions so you can decide where to trust, test, or intervene.

  • 01

    Claude Code is the deepest-tested integration. Other targets have less real-world exercise today.

  • 02

    Blast-radius analysis is heuristic. It guides review; it is not a formal dependency proof.

  • 03

    Guardrails are not a sandbox. Keep permissions, review, and backups appropriate to the work.

06 / Start hereAbout sixty seconds

Give the next agent a better starting point.

Install ForgeKit, run forge init in your repository, and keep one shared operating context across every tool.

Open the quickstart
forgekit / install
 /plugin marketplace add CodeWithJuber/forgekit
  /plugin install forgekit
-

Recommended · ambient guards on every prompt

+

Recommended · ambient guards on every prompt

diff --git a/landing/media/evidence-bg.webp b/landing/media/evidence-bg.webp new file mode 100644 index 00000000..bcaf38c1 Binary files /dev/null and b/landing/media/evidence-bg.webp differ diff --git a/mintlify/changelog/overview.mdx b/mintlify/changelog/overview.mdx index ea6c023b..68eeb690 100644 --- a/mintlify/changelog/overview.mdx +++ b/mintlify/changelog/overview.mdx @@ -4,6 +4,10 @@ description: "Every Forge release, newest first: the headline of each change, wi rss: true --- + + A timeline of star medallions, the newest glowing brightest: every release, newest first. + + Every release, newest first. Each entry lists the headline of every change it shipped; **Full notes** opens that release in [CHANGELOG.md](https://github.com/CodeWithJuber/forgekit/blob/HEAD/CHANGELOG.md), which has the @@ -14,6 +18,22 @@ This page is generated from `CHANGELOG.md` by `forge docs render`, and `forge do CI when it falls behind, so it cannot drift from the release notes again. {/* forge:render:changelog:begin (generated by `forge docs render` — do not edit) */} + + +**Fixed** + +- **Build caches under `.cache/` stay out of the import graph.** +- **The landing page's hero says ten native targets.** + +**Added** + +- **Every diagram is now rendered by [Archify](https://github.com/tt-a1i/archify) from a typed source.** +- **Illustrations for the docs site and the landing page, and a new social card.** + +[Full notes for Unreleased →](https://github.com/CodeWithJuber/forgekit/blob/HEAD/CHANGELOG.md#unreleased) + + + **Security** diff --git a/mintlify/concepts/config-compiler.mdx b/mintlify/concepts/config-compiler.mdx index 46e81a3f..25d30e09 100644 --- a/mintlify/concepts/config-compiler.mdx +++ b/mintlify/concepts/config-compiler.mdx @@ -3,23 +3,19 @@ title: "The four-layer config compiler" description: "Author the substrate once; forge sync compiles it into each tool's native config. The four layers are how the brain is expressed; the compiler is how it is delivered." --- + + One eight-point star sends ten ember lines to ten matching tiles: one source, native config for every tool. + + You author the substrate once. `forge sync` compiles that source into each tool's native config. The four layers are _how the brain is expressed_; the compiler is _how it is delivered_. -```mermaid -%%{init: {'theme':'base','themeVariables':{'primaryColor':'#201a15','primaryTextColor':'#f2ede7','primaryBorderColor':'#372c22','lineColor':'#f26430','secondaryColor':'#272019','tertiaryColor':'#171310','edgeLabelBackground':'#201a15','clusterBkg':'#171310','clusterBorder':'#4a3b2e','fontFamily':'ui-sans-serif, system-ui, sans-serif','fontSize':'14px'},'flowchart':{'curve':'basis','padding':10,'nodeSpacing':36,'rankSpacing':44}}}%% -flowchart TD - S["source/ · rules.json · substrate.json · mcp.json"] -->|"forge sync — content-hash + DO-NOT-EDIT headers"| N["native configs · CLAUDE.md · AGENTS.md · .cursor · .gemini · .aider"] - S -. configures .-> L - subgraph L["the four layers"] - direction LR - T["tools · model-invoked skills"] - C["crew · isolated sub-agents"] - G["guards · deterministic hooks"] - M["mcp · atlas + substrate server"] - end -``` + + forgekit's architecture: forge sync compiles source/ into native configs for ten tools and configures the four layers (tools, crew, guards, mcp); local events write content-addressed claims to the PCM ledger, independent oracles move their confidence, and teammate ledgers merge through git union-merge + + +[Open the interactive diagram](https://codewithjuber.github.io/forgekit/diagrams/system.html): pan, zoom, search, and trace any node. ## One source, many emitters diff --git a/mintlify/concepts/cross-session-memory.mdx b/mintlify/concepts/cross-session-memory.mdx index a9c7ab49..293dc29e 100644 --- a/mintlify/concepts/cross-session-memory.mdx +++ b/mintlify/concepts/cross-session-memory.mdx @@ -8,6 +8,14 @@ artifacts that depend on it) and **session amnesia** (the next session re-assume this one knew). Instructions raise the _probability_ of correct behavior; deterministic hooks guarantee a _floor_. +Where those hooks run in one Claude Code session: + + + One Claude Code session with forgekit's hooks: SessionStart injects learned lessons and the last handoff; UserPromptSubmit runs cortex and preflight against the cached atlas and returns an advisory; PreToolUse runs protect-paths, cost-budget, doom-loop and cortex pre-edit and allows or denies the call; PostToolUse formats, redacts secrets and captures evidence; Stop runs the completion gate, lean guard and session learner and distills lessons into the ledger + + +[Open the interactive diagram](https://codewithjuber.github.io/forgekit/diagrams/hook-sequence.html): pan, zoom, search, and trace any node. + ## Session anchoring On `SessionStart` (`src/session.js`), Forge records `HEAD` once per session, prunes diff --git a/mintlify/concepts/model-routing.mdx b/mintlify/concepts/model-routing.mdx index aebe7250..c2ec2257 100644 --- a/mintlify/concepts/model-routing.mdx +++ b/mintlify/concepts/model-routing.mdx @@ -3,6 +3,10 @@ title: "Model routing" description: "A deterministic, diffable rubric picks the cheapest capable model tier before dispatch — each tier a model family, resolved to the newest live model with a fail-safe fallback." --- + + One ember line splits into three paths through gates of rising brightness: a cascade that escalates only on failure. + + Forge recommends the cheapest capable model for a task **before** dispatch, from a deterministic rubric you can read in the repo (`src/route.js`). Unlike a gateway that decides inside the proxy at request time, the routing decision is visible and @@ -99,6 +103,12 @@ A separate, opt-in router: it recommends one model, or a cascade that escalates check, across every provider in the registry, minimising **expected** cost for the success you ask for. + + forge route universal: the task's 12 features and each model's P(solve) and expected cost select the cheapest cascade that meets the objective, or report INFEASIBLE with a labeled least-bad fallback; each attempt is checked by tests or forge verify, a failure escalates to the next model, and forge route outcome records each outcome for forge route fit, which refits a local estimate shrunk toward the shipped prior + + +[Open the interactive diagram](https://codewithjuber.github.io/forgekit/diagrams/router-cascade.html): pan, zoom, search, and trace any node. + ```bash forge route universal "" # match the best single model forge route universal "" --objective target:0.8 # or value:<$>, budget:<$> diff --git a/mintlify/concepts/pre-action-gate.mdx b/mintlify/concepts/pre-action-gate.mdx index f681cd5e..f81dfd6d 100644 --- a/mintlify/concepts/pre-action-gate.mdx +++ b/mintlify/concepts/pre-action-gate.mdx @@ -3,32 +3,21 @@ title: "The pre-action gate" description: "forge substrate runs one ordered pass of checks before the model edits code and returns a single verdict — assumptions, routing, impact, scope, memory, and verification." --- + + A geometric arch with ember light passing through onto the paths beyond: each task is checked before the agent edits. + + **Cognitive substrate** — the layer that runs _before_ the model edits code. `forge substrate ""` (and the MCP tool `substrate_check`) runs one ordered pass of checks and returns a single verdict. It composes the individually-callable stages — `preflight`, `route`, `atlas`, `impact`, `reuse`, `context`, `scope`, `lean`, `anchor`, `verify` — into one pre-action contract. -```mermaid -%%{init: {'theme':'base','themeVariables':{'primaryColor':'#201a15','primaryTextColor':'#f2ede7','primaryBorderColor':'#372c22','lineColor':'#f26430','secondaryColor':'#272019','tertiaryColor':'#171310','edgeLabelBackground':'#201a15','clusterBkg':'#171310','clusterBorder':'#4a3b2e','fontFamily':'ui-sans-serif, system-ui, sans-serif','fontSize':'14px'},'flowchart':{'curve':'basis','padding':10,'nodeSpacing':36,'rankSpacing':44}}}%% -flowchart TD - RE["referenced entities"] --> INTAKE - subgraph INTAKE["intake"] - direction LR - PF["preflight · assumption gap"] --> RT["route · cheapest tier"] - end - INTAKE --> ANALYSIS - subgraph ANALYSIS["analysis"] - direction LR - AT["atlas · code graph"] --> IM["impact · blast radius"] --> PT["predict · failing tests"] --> RU["reuse · cache hit?"] - end - ANALYSIS --> SAFETY - subgraph SAFETY["safety + fit"] - direction LR - CX["context · completeness gate"] --> SC["scope · coupled files"] --> ME["memory · recall + lessons"] --> MN["minimality · lean footprint"] --> GA["goal-anchor · drift check"] - end - SAFETY --> VD["verdict"] -``` + + The pre-action gate: referenced entities pass through intake (preflight, route), analysis (atlas, impact, predict, reuse) and safety and fit (context, scope, memory, minimality, goal-anchor) to one verdict + + +[Open the interactive diagram](https://codewithjuber.github.io/forgekit/diagrams/pre-action-gate.html): pan, zoom, search, and trace any node. ## The three phases diff --git a/mintlify/concepts/proof-carrying-memory.mdx b/mintlify/concepts/proof-carrying-memory.mdx index b4be7738..3c259639 100644 --- a/mintlify/concepts/proof-carrying-memory.mdx +++ b/mintlify/concepts/proof-carrying-memory.mdx @@ -3,6 +3,10 @@ title: "Proof-carrying memory" description: "Every stored fact, lesson, or reuse artifact is a claim that carries its own evidence — trusted only once independent oracles raise its confidence above a floor." --- + + A stack of tiles sealed with star stamps, some glowing and some dim, each linked to evidence markers: a claim is trusted only as far as its evidence. + + **Proof-carrying memory (PCM)** — every stored fact, lesson, or reuse artifact is a _claim_ that carries its own evidence. It is trusted only once independent oracles (tests, CI, a human accept/revert) raise its confidence above a floor. A wrong lesson @@ -24,21 +28,11 @@ claims into `.forge/ledger/`. The ledger is now the **default and only** store: materializes from the ledger. (`FORGE_LEDGER_ONLY=0` restores the legacy file store as a one-release escape hatch.) -```mermaid -%%{init: {'theme':'base','themeVariables':{'primaryColor':'#201a15','primaryTextColor':'#f2ede7','primaryBorderColor':'#372c22','lineColor':'#f26430','secondaryColor':'#272019','tertiaryColor':'#171310','edgeLabelBackground':'#201a15','clusterBkg':'#171310','clusterBorder':'#4a3b2e','fontFamily':'ui-sans-serif, system-ui, sans-serif','fontSize':'14px'},'flowchart':{'curve':'basis','padding':10,'nodeSpacing':36,'rankSpacing':44}}}%% -flowchart LR - subgraph EV["local events"] - direction TB - E1["recall / remember"] - E2["cortex lesson"] - E3["reuse mint"] - E4["diagnose"] - end - EV -->|"content-addressed claims"| LG["(.forge/ledger)"] - O["independent oracles · tests · CI · human accept/revert"] -->|"append evidence · move confidence"| LG - TM["teammate ledgers"] <-->|"git union-merge · conflict-free"| LG - LG --> RV["merged read view · recall list · lesson inject · brain index"] -``` + + Proof-carrying memory: local events write claims and independent oracles append evidence to .forge/ledger/; a merged read view feeds the recall list, lesson injection and the brain index; teammate ledgers merge through git union-merge and forge ledger merge + + +[Open the interactive diagram](https://codewithjuber.github.io/forgekit/diagrams/ledger-flow.html): pan, zoom, search, and trace any node. ## Why it converges without conflicts @@ -79,6 +73,14 @@ Unverifiable evidence is rejected by a closed `ORACLES` table (`src/ledger.js`). Unreviewed knowledge decays toward _uncertainty_, not deletion — dormant claims are kept for audit, never silently removed. +A claim's life, from mint to tombstone: + + + Claim lifecycle: a minted claim starts uncertain; confirmations raise it to trusted and contradictions lower it; below val 0.35 it goes dormant until a later confirmation; idle, duplicate and dormant claims are archived with a reason and new evidence brings them back; forge ledger retract tombstones a claim permanently; a reworded lesson is minted as a new claim + + +[Open the interactive diagram](https://codewithjuber.github.io/forgekit/diagrams/claim-lifecycle.html): pan, zoom, search, and trace any node. + ## The ledger surface ```bash @@ -110,16 +112,11 @@ One event is one vote. Abbreviations of one commit count once. A reworded lesson trust only when the rewrite is equivalent. Similar-but-opposite rules are reported as conflicts, never merged. -```mermaid -%%{init: {'theme':'base','themeVariables':{'primaryColor':'#201a15','primaryTextColor':'#f2ede7','primaryBorderColor':'#372c22','lineColor':'#f26430','secondaryColor':'#272019','tertiaryColor':'#171310','edgeLabelBackground':'#201a15','clusterBkg':'#171310','clusterBorder':'#4a3b2e','fontFamily':'ui-sans-serif, system-ui, sans-serif','fontSize':'14px'},'flowchart':{'curve':'basis','padding':10,'nodeSpacing':36,'rankSpacing':44}}}%% -flowchart LR - SP["spec"] --> FP["fingerprint · MinHash + LSH"] - FP --> LD["match ladder · exact to near to adapt to miss"] - LD --> GT{"confidence >= floor AND deps resolve?"} - GT -->|"yes"| SV["serve · proof holds"] - GT -->|"miss"| GN["generate"] - GN -->|"mint claim"| MT["(.forge/ledger)"] -``` + + Reuse cache: a spec is looked up by a lossless exact key, then by MinHash and LSH similarity checked by a semantic guard (operators, numbers, literals); a hit is served only while the proof check holds (confidence at least 0.6) and is flagged as not revalidated when there is no atlas; a match that differs is only an adapt-tier candidate; a miss is generated, verified and minted as a claim into .forge/ledger/ + + +[Open the interactive diagram](https://codewithjuber.github.io/forgekit/diagrams/reuse-cache.html): pan, zoom, search, and trace any node. The MinHash near-match is weak on very short specs. An optional embeddings backend diff --git a/mintlify/concepts/verification-gates.mdx b/mintlify/concepts/verification-gates.mdx index 26422369..6f2a9e7d 100644 --- a/mintlify/concepts/verification-gates.mdx +++ b/mintlify/concepts/verification-gates.mdx @@ -3,6 +3,10 @@ title: "Verification gates" description: "Independent verification, the hallucinated-symbol flag, spec-as-contract, and the skill-gate — checks you can run that reduce, but never certify, correctness." --- + + Concentric geometric rings like layered sieves, with only a few particles reaching the glowing center: independent checks that reduce silent misses. + + Nothing is "done" without a check you can run — a test, a build exit code, a screenshot. With per-task miss rate `1 − p`, silent misses fall to `(1 − p)` times the chance that no check fires on the miss: `(1 − p)(1 − c)` for one gate with catch rate `c`. A further gate @@ -21,6 +25,12 @@ so the residual stays `(1 − p)(1 − c_max)` rather than a product. An independent gate: it runs the repo's real tests, flags hallucinated symbols, and checks provenance. + + forge verify: suites are planned for the root and every nested package that declares one; the code state (HEAD, staged and unstaged diffs, untracked files) is captured before and after each suite runs in its own directory, and a change during the run makes the result INCOMPLETE; the verdict is PASS, FAIL, INCOMPLETE or NOT_CONFIGURED; a hallucinated-symbol check runs against the atlas; the result is sealed in .forge/provenance.json and a verifier event is appended to .forge/verify-events.jsonl + + +[Open the interactive diagram](https://codewithjuber.github.io/forgekit/diagrams/verify-pipeline.html): pan, zoom, search, and trace any node. + ```bash forge verify # tests + hallucinated-symbol + provenance forge verify --deep # multi-lens consensus — several independent checks must agree diff --git a/mintlify/guides/team-memory.mdx b/mintlify/guides/team-memory.mdx index 473caae8..19bde29f 100644 --- a/mintlify/guides/team-memory.mdx +++ b/mintlify/guides/team-memory.mdx @@ -3,6 +3,10 @@ title: "Team memory with the ledger" description: "Fold a teammate's ledger in, conflict-free, over plain git — no server, no sync service, just files that converge in any order." --- + + Two geometric lattices interlocking seamlessly in the middle: teammates' ledgers merging without conflicts. + + Everything the substrate learns — cortex lessons, `forge remember` facts, verified reuse artifacts — lands as content-addressed claims in a git-native ledger (`.forge/ledger/`) built to merge without conflicts. There is no server and no sync service; it is just @@ -36,14 +40,11 @@ the same identity for the same knowledge. The merge is a join-semilattice — property-tested to be commutative, associative, and idempotent — so two teammates' ledgers converge to the same state no matter who syncs first. -```mermaid -%%{init: {'theme':'base','themeVariables':{'primaryColor':'#201a15','primaryTextColor':'#f2ede7','primaryBorderColor':'#372c22','lineColor':'#f26430','secondaryColor':'#272019','tertiaryColor':'#171310','edgeLabelBackground':'#201a15','clusterBkg':'#171310','clusterBorder':'#4a3b2e','fontFamily':'ui-sans-serif, system-ui, sans-serif','fontSize':'14px'},'flowchart':{'curve':'basis','padding':10,'nodeSpacing':36,'rankSpacing':44}}}%% -flowchart LR - A["your ledger"] <-->|"git union-merge · conflict-free"| B["teammate ledger"] - A --> M["merged read view"] - B --> M - M --> R["recall list · lesson inject · brain index"] -``` + + Proof-carrying memory: local events write claims and independent oracles append evidence to .forge/ledger/; teammate ledgers merge through git union-merge and forge ledger merge; a merged read view feeds the recall list, lesson injection and the brain index + + +[Open the interactive diagram](https://codewithjuber.github.io/forgekit/diagrams/ledger-flow.html): pan, zoom, search, and trace any node. Identical knowledge minted independently converges to **one** claim with every author diff --git a/mintlify/guides/zero-config-onboarding.mdx b/mintlify/guides/zero-config-onboarding.mdx index c0abfc06..e6985ad7 100644 --- a/mintlify/guides/zero-config-onboarding.mdx +++ b/mintlify/guides/zero-config-onboarding.mdx @@ -8,16 +8,11 @@ in about five minutes. Install once, configure a repo once, do a task, and the l paying off on day two. (It is low-configuration, not zero-configuration: you still install the CLI, run `forge init` in each repo, and some paths assume Bash, Git, and `jq`.) -```mermaid -%%{init: {'theme':'base','themeVariables':{'primaryColor':'#201a15','primaryTextColor':'#f2ede7','primaryBorderColor':'#372c22','lineColor':'#f26430','secondaryColor':'#272019','tertiaryColor':'#171310','edgeLabelBackground':'#201a15','clusterBkg':'#171310','clusterBorder':'#4a3b2e','fontFamily':'ui-sans-serif, system-ui, sans-serif','fontSize':'14px'},'flowchart':{'curve':'basis','padding':10,'nodeSpacing':36,'rankSpacing':44}}}%% -flowchart TD - I["forge init"] --> Cfg["your tools configured from one source"] - Cfg --> Work["you work as usual"] - Work --> Gate["substrate checks each task · ask first? · which model? · what breaks?"] - Gate --> Edit["agent edits, with guardrails"] - Edit --> Learn["cortex learns from corrections"] - Learn -.->|next task is smarter| Work -``` + + Guided onboarding: forge init configures your tools from one source; then for each task you work as usual, the substrate checks whether to ask first, which model to use and what breaks, the agent edits with guardrails, and cortex learns from corrections so the next task is smarter + + +[Open the interactive diagram](https://codewithjuber.github.io/forgekit/diagrams/onboarding-loop.html): pan, zoom, search, and trace any node. ## 1. Install (once) diff --git a/mintlify/images/diagrams/claim-lifecycle.svg b/mintlify/images/diagrams/claim-lifecycle.svg new file mode 100644 index 00000000..197d15ef --- /dev/null +++ b/mintlify/images/diagrams/claim-lifecycle.svg @@ -0,0 +1,616 @@ + + + Claim lifecycle in the ledger + A lifecycle diagram generated by Archify. + + + + + + + + + + + + + + + + + + + + + + + + + 01 / Live · retrievable + + 02 / Out of retrieval + + 03 / Permanent + + + + + + + + + + + + + + + + + + + Reworded lesson · keeps trust only if equivalent · Live · retrievable · up to case · space · punct. + + + + Reworded lesson + keeps trust only if equivalent + up to case · space · punct. + + + + Minted · 0.5 prior · Live · retrievable · candidate + + + + Minted + 0.5 prior + candidate + + + + Uncertain · val 0.35–0.65 · Live · retrievable + + + + Uncertain + val 0.35–0.65 + + + + Trusted · val ≥ 0.65 · Live · retrievable · reuse serve floor 0.6 + + + + Trusted + val ≥ 0.65 + reuse serve floor 0.6 + + + + Dormant · val < 0.35 · latched · Out of retrieval · kept for audit, never retrieved + + + + Dormant + val < 0.35 · latched + kept for audit, never retrieved + + + + Archived · .forge/ledger/attic/ · Out of retrieval · tombstoned · dormant · idle · duplicate + + + + Archived + .forge/ledger/attic/ + tombstoned · dormant · idle · duplicate + + + + Tombstoned · permanent · Permanent · retracted by a human + + + + Tombstoned + permanent + retracted by a human + + + + + + + + confirms raise val + + + + contradicts lower val + + + + contradicts: val < 0.35 + + + + later confirmation + + + + dormant + + + + idle · duplicate + past learned cut-off · survivor named + + + + forge ledger retract + + + + new evidence brings it back + + + + + Legend + + + start + + + + active state + + + + failure / exit + + + + neutral + + + + external + + + \ No newline at end of file diff --git a/mintlify/images/diagrams/hook-sequence.svg b/mintlify/images/diagrams/hook-sequence.svg new file mode 100644 index 00000000..9eee0d3e --- /dev/null +++ b/mintlify/images/diagrams/hook-sequence.svg @@ -0,0 +1,662 @@ + + + One Claude Code session with forgekit's hooks + A sequence diagram generated by Archify. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SessionStart: recall-load · cortex session-start + + + + + + + + inject learned lessons + last handoff (.forge/state.md) + + + + + + + + prompt + + + + + + + + UserPromptSubmit: cortex prompt + preflight + + + + + + + + reads CACHED atlas only; never builds it in a hook + + + + + + + + substrate advisory: suggested model · impact · verify checklist + + + + + + + + PreToolUse: protect-paths · cost-budget · doom-loop · cortex pre-edit + + + + + + + + allow | deny + + + + + + + + PostToolUse: format-on-edit · secret-redact · cortex capture + + + + + + + + cortex capture: signals + evidence + + + + + + + + Stop: completion-gate · lean-guard · session-learner · cortex stop + + + + + + + + cortex stop: distill lessons into the ledger + + + + + + + + session ends | agent told what is missing + + + + + + + Session start + + + + Prompt + + + + Tool call + + + + Stop + + + + + Developer · Sequence participant + + + + Developer + + + + Claude Code (agent) · Sequence participant + + + + Claude Code (agent) + + + + forgekit hooks · Sequence participant + + + + forgekit hooks + + + + .forge/ state · Sequence participant + + + + .forge/ state + + + + + Legend + + + request + + + + return + + + + security + + + + default message + + + \ No newline at end of file diff --git a/mintlify/images/diagrams/ledger-flow.svg b/mintlify/images/diagrams/ledger-flow.svg new file mode 100644 index 00000000..d629008b --- /dev/null +++ b/mintlify/images/diagrams/ledger-flow.svg @@ -0,0 +1,623 @@ + + + Proof-carrying memory: how claims gain and lose trust + A data-flow diagram generated by Archify. + + + + + + + + + + + + + + + + + + + + + + + + + 01 / This machine · sources + + + 02 / This machine · ledger + + + 03 / This machine · read + + + 04 / This machine · feeds + + + 05 / Team · git + + + + + + + + + + + + + + Independent oracles · tests · CI · 01 / This machine · sources · human accept / revert + + + + Independent oracles + tests · CI + human accept / revert + + + + Local events · recall / remember · cortex lesson · 01 / This machine · sources · reuse mint · diagnose + + + + Local events + recall / remember · cortex lesson + reuse mint · diagnose + + + + .forge/ledger/ · content-addressed claims · 02 / This machine · ledger · append-only evidence logs + + + + .forge/ledger/ + content-addressed claims + append-only evidence logs + + + + Merged read view · 03 / This machine · read + + + + Merged read view + + + + Recall list · 04 / This machine · feeds + + + + Recall list + + + + Lesson injection · 04 / This machine · feeds + + + + Lesson injection + + + + Brain index · 04 / This machine · feeds + + + + Brain index + + + + Teammate ledgers · 05 / Team · git + + + + Teammate ledgers + + + + + + writes claims + + + + append evidence + moves confidence + + + + claims + evidence + + + + feeds + + + + feeds + + + + feeds + + + + git union-merge + conflict-free + + + + forge ledger merge / sync + + + + + Legend + + + primary data + + + + data store + + + + data flow + + + \ No newline at end of file diff --git a/mintlify/images/diagrams/onboarding-loop.svg b/mintlify/images/diagrams/onboarding-loop.svg new file mode 100644 index 00000000..c99ed494 --- /dev/null +++ b/mintlify/images/diagrams/onboarding-loop.svg @@ -0,0 +1,576 @@ + + + Guided onboarding: what happens after forge init + A workflow diagram generated by Archify. + + + + + + + + + + + + + + + + + + + + + + + + + 01 / Setup + + + 02 / Each task + + + 03 / Learning + + + + + + + + + + + + + + + + + + forge init · Setup + + + + forge init + + + + Your tools configured · from one source · Setup + + + + Your tools configured + from one source + + + + You work as usual · Each task + + + + You work as usual + + + + Substrate checks · ask first? which model? what breaks? · Each task + + + + Substrate checks + ask first? which model? what breaks? + + + + Agent edits · with guardrails · Each task + + + + Agent edits + with guardrails + + + + Cortex learns · from corrections · Learning + + + + Cortex learns + from corrections + + + + + + + + + + next task is smarter + + + + + + Legend + + + User UI + + + + Agent logic + + + + Policy + + + + Tool action + + + + Context / trace + + + \ No newline at end of file diff --git a/mintlify/images/diagrams/pre-action-gate.svg b/mintlify/images/diagrams/pre-action-gate.svg new file mode 100644 index 00000000..841305b4 --- /dev/null +++ b/mintlify/images/diagrams/pre-action-gate.svg @@ -0,0 +1,656 @@ + + + The pre-action gate: one deterministic pass before the agent edits + A workflow diagram generated by Archify. + + + + + + + + + + + + + + + + + + + + + + + + + 01 / Intake + + + 02 / Analysis + + + 03 / Safety + fit + + + + + + + + + + + + + + + + + + + + + + + + Referenced entities · from the task · Intake + + + + Referenced entities + from the task + + + + preflight · assumption gap · Intake + + + + preflight + assumption gap + + + + route · cheapest capable tier · Intake + + + + route + cheapest capable tier + + + + atlas · code graph · Analysis + + + + atlas + code graph + + + + impact · blast radius · Analysis + + + + impact + blast radius + + + + predict · likely failing tests · Analysis + + + + predict + likely failing tests + + + + reuse · cache hit? · Analysis + + + + reuse + cache hit? + + + + Verdict · advisory by default · Safety + fit + + + + Verdict + advisory by default + + + + goal-anchor · drift check · Safety + fit + + + + goal-anchor + drift check + + + + minimality · lean footprint · Safety + fit + + + + minimality + lean footprint + + + + memory · recall + lessons · Safety + fit + + + + memory + recall + lessons + + + + scope · coupled files · Safety + fit + + + + scope + coupled files + + + + context · completeness gate · Safety + fit + + + + context + completeness gate + + + + + + + + + + + + + + + + + + + Legend + + + Policy + + + + Context / trace + + + \ No newline at end of file diff --git a/mintlify/images/diagrams/reuse-cache.svg b/mintlify/images/diagrams/reuse-cache.svg new file mode 100644 index 00000000..d4c78b53 --- /dev/null +++ b/mintlify/images/diagrams/reuse-cache.svg @@ -0,0 +1,668 @@ + + + Reuse cache: serve only while the proof holds + A workflow diagram generated by Archify. + + + + + + + + + + + + + + + + + + + + + + + + + 01 / Lookup ladder + + + 02 / Serve gate + + + + EX / Miss path + + + + + + + + + + + + + + + + + + + + + + + Spec · the task text · Lookup ladder + + + + Spec + the task text + + + + Exact tier · lossless except whitespace · Lookup ladder · Unicode-normalized; case kept + + + + Exact tier + lossless except whitespace + Unicode-normalized; case kept + + + + Near tier · MinHash + LSH similarity · Lookup ladder + + + + Near tier + MinHash + LSH similarity + + + + Semantic guard · operators · numbers · literals · Lookup ladder · identifiers · paths · negation + + + + Semantic guard + operators · numbers · literals + identifiers · paths · negation + + + + Adapt tier · a match that differs · Lookup ladder + + + + Adapt tier + a match that differs + + + + Proof check · serve floor: confidence ≥ 0.6 · Serve gate · same sha256 · dep contracts hold + + + + Proof check + serve floor: confidence ≥ 0.6 + same sha256 · dep contracts hold + + + + Served · proof holds · adds no evidence · Serve gate · no atlas → requiresRevalidation + + + + Served + proof holds · adds no evidence + no atlas → requiresRevalidation + + + + forge reuse mint · claim → .forge/ledger/ · Miss path · available next time + + + + forge reuse mint + claim → .forge/ledger/ + available next time + + + + Verify · Miss path + + + + Verify + + + + Generate · Miss path + + + + Generate + + + + + + otherwise: miss + + + + fails + + + + all hold + + + + hit + + + + otherwise + + + + + differs + + + + agrees: hit + + + + lookup + + + + similar + + + + + + Legend + + + Agent logic + + + + Policy + + + + Tool action + + + + Context / trace + + + + External system + + + \ No newline at end of file diff --git a/mintlify/images/diagrams/router-cascade.svg b/mintlify/images/diagrams/router-cascade.svg new file mode 100644 index 00000000..307cc53f --- /dev/null +++ b/mintlify/images/diagrams/router-cascade.svg @@ -0,0 +1,646 @@ + + + forge route universal: a cascade under an explicit objective + A workflow diagram generated by Archify. + + + + + + + + + + + + + + + + + + + + + + + + + + EX / Fallbacks + + + 02 / Route (advice) + + + 03 / Run the cascade + + + 04 / Record + fit + + + + + + + + + + + + + + + + + + + + + + + INFEASIBLE · exit 1 · labeled least-bad fallback · Fallbacks + + + + INFEASIBLE · exit 1 + labeled least-bad fallback + + + + Escalate · next model in the cascade · Fallbacks + + + + Escalate + next model in the cascade + + + + forge route universal · "<task>" → 12 task features · Route (advice) + + + + forge route universal + "<task>" → 12 task features + + + + Per model · P(solve) + expected cost · Route (advice) + + + + Per model + P(solve) + expected cost + + + + Cheapest cascade · that meets the objective · Route (advice) + + + + Cheapest cascade + that meets the objective + + + + Recommendation · advice only if no provider id · Route (advice) + + + + Recommendation + advice only if no provider id + + + + Attempt 1 · first model in cascade · Run the cascade + + + + Attempt 1 + first model in cascade + + + + Check · tests / forge verify · Run the cascade + + + + Check + tests / forge verify + + + + Done · Run the cascade + + + + Done + + + + forge route outcome · self-reported unless --verify-run · Record + fit + + + + forge route outcome + self-reported unless --verify-run + + + + forge route fit · MAP shrinkage to shipped prior · Record + fit + + + + forge route fit + MAP shrinkage to shipped prior + + + + + + + fail + + + + pass + + + + feasible + + + + infeasible + + + + + + + + + each outcome + + + + + Legend + + + User UI + + + + Agent logic + + + + External system + + + \ No newline at end of file diff --git a/mintlify/images/diagrams/system.svg b/mintlify/images/diagrams/system.svg new file mode 100644 index 00000000..45a5ca4f --- /dev/null +++ b/mintlify/images/diagrams/system.svg @@ -0,0 +1,639 @@ + + + forgekit: one source, native configs, enforced guards, evidence-carrying memory + An architecture diagram generated by Archify. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + source/ · the one source of truth · rules.json · substrate.json · mcp.json · Architecture component + + + + source/ · the one source of truth + rules.json · substrate.json · mcp.json + + + + forge sync · content hash · DO-NOT-EDIT headers · Architecture component + + + + forge sync + content hash · DO-NOT-EDIT headers + + + + Native configs for ten tools · CLAUDE.md · AGENTS.md · .cursor · .gemini · .aider · … · Architecture component + + + + Native configs for ten tools + CLAUDE.md · AGENTS.md · .cursor · .gemini · .aider · … + + + + tools · model-invoked skills · the four layers + + + + tools + model-invoked skills + + + + crew · isolated sub-agents · the four layers + + + + crew + isolated sub-agents + + + + guards · enforced · deterministic hooks · the four layers + + + + guards · enforced + deterministic hooks + + + + mcp · atlas + substrate MCP server · the four layers + + + + mcp + atlas + substrate MCP server + + + + Local events · cortex · recall · reuse · diagnose · Architecture component + + + + Local events + cortex · recall · reuse · diagnose + + + + PCM ledger · .forge/ledger/ · Architecture component + + + + PCM ledger + .forge/ledger/ + + + + Teammate ledgers · Architecture component + + + + Teammate ledgers + + + + Independent oracles · tests · CI · human accept/revert · Architecture component + + + + Independent oracles + tests · CI · human accept/revert + + + + + + + emits + + + + configures all four + + + + content-addressed claims + + + + move confidence + + + + git union-merge, conflict-free + + + + + + + the four layers + + + + + Legend + + + Backend + + + + Database + + + + Security + + + + Message bus + + + + External + + + \ No newline at end of file diff --git a/mintlify/images/diagrams/verify-pipeline.svg b/mintlify/images/diagrams/verify-pipeline.svg new file mode 100644 index 00000000..660f55a0 --- /dev/null +++ b/mintlify/images/diagrams/verify-pipeline.svg @@ -0,0 +1,612 @@ + + + forge verify: a PASS bound to the code that was tested + A workflow diagram generated by Archify. + + + + + + + + + + + + + + + + + + + + + + + + + 01 / Code state + + + 02 / Suites + + + 03 / .forge/ + + + + + + + + + + + + + + + + + + + + Pre-run state · HEAD · diffs · untracked files · Code state · untracked: path/mode/size/sha256 + + + + Pre-run state + HEAD · diffs · untracked files + untracked: path/mode/size/sha256 + + + + Post-run state · changed → INCOMPLETE (mutated) · Code state + + + + Post-run state + changed → INCOMPLETE (mutated) + + + + Plan suites · root + nested packages with a suite · Suites · scripts.test · pytest config · go.mod · … + + + + Plan suites + root + nested packages with a suite + scripts.test · pytest config · go.mod · … + + + + Run each suite · in its own directory · Suites + + + + Run each suite + in its own directory + + + + Aggregate · PASS · FAIL · INCOMPLETE · NOT_CONFIGURED · Suites + + + + Aggregate + PASS · FAIL · INCOMPLETE · NOT_CONFIGURED + + + + Hallucinated-symbol check · against the atlas · Suites + + + + Hallucinated-symbol check + against the atlas + + + + .forge/forge.config.json · under verify · .forge/ · workspaces · exclude · generated + + + + .forge/forge.config.json + under verify + workspaces · exclude · generated + + + + .forge/verify-events.jsonl · verifier event appended · .forge/ + + + + .forge/verify-events.jsonl + verifier event appended + + + + .forge/provenance.json · MAC-sealed stamp · .forge/ · bound to the pre-run state + + + + .forge/provenance.json + MAC-sealed stamp + bound to the pre-run state + + + + + + + + + + + + + + + Legend + + + Policy + + + + Tool action + + + + Context / trace + + + \ No newline at end of file diff --git a/mintlify/images/illustrations/changelog-header.webp b/mintlify/images/illustrations/changelog-header.webp new file mode 100644 index 00000000..701b85e1 Binary files /dev/null and b/mintlify/images/illustrations/changelog-header.webp differ diff --git a/mintlify/images/illustrations/config-compiler.webp b/mintlify/images/illustrations/config-compiler.webp new file mode 100644 index 00000000..78f68643 Binary files /dev/null and b/mintlify/images/illustrations/config-compiler.webp differ diff --git a/mintlify/images/illustrations/model-routing.webp b/mintlify/images/illustrations/model-routing.webp new file mode 100644 index 00000000..e4a13b7d Binary files /dev/null and b/mintlify/images/illustrations/model-routing.webp differ diff --git a/mintlify/images/illustrations/pre-action-gate.webp b/mintlify/images/illustrations/pre-action-gate.webp new file mode 100644 index 00000000..50c7d6d8 Binary files /dev/null and b/mintlify/images/illustrations/pre-action-gate.webp differ diff --git a/mintlify/images/illustrations/proof-carrying-memory.webp b/mintlify/images/illustrations/proof-carrying-memory.webp new file mode 100644 index 00000000..6f5664ed Binary files /dev/null and b/mintlify/images/illustrations/proof-carrying-memory.webp differ diff --git a/mintlify/images/illustrations/team-memory.webp b/mintlify/images/illustrations/team-memory.webp new file mode 100644 index 00000000..12088124 Binary files /dev/null and b/mintlify/images/illustrations/team-memory.webp differ diff --git a/mintlify/images/illustrations/verification-gates.webp b/mintlify/images/illustrations/verification-gates.webp new file mode 100644 index 00000000..9a36b2ad Binary files /dev/null and b/mintlify/images/illustrations/verification-gates.webp differ diff --git a/scripts/build-pages.mjs b/scripts/build-pages.mjs index c274a93d..cd9cb3a7 100644 --- a/scripts/build-pages.mjs +++ b/scripts/build-pages.mjs @@ -195,7 +195,7 @@ export function render(d) { description: d.description, offers: { "@type": "Offer", price: "0" }, }); - return `forgekit status — live repository data

${esc(d.name)} · v${esc(d.version)} · Node ${esc(d.node)}

Live status, straight from the repository.

${esc(d.description)}

Install in 60 seconds Read the docs

${esc(d.license)} license${esc(d.deps)} runtime dependencies${esc(d.branch)} @ ${esc(d.commit)}${live}
${esc(d.impact)}
blast-radius lookup

Measured from this repo's benchmark report, not a marketing placeholder.

reports/benchmarks.md

${esc(d.speed)}
pre-action gate

Assumptions, routing, reuse, context, impact, scope, and anchoring.

reports/benchmarks.md

${esc(d.saved.match(/^[\d.]+\s*%?/)?.[0] ?? d.saved)}
${esc(d.saved.replace(/^[\d.]+\s*%?\s*/, "") || "routing signal")}

Held-out result: on 80 pre-registered tasks the white-paper router spent this much more than always-premium; its 62.1% demo saving is refuted.

research/empirical-refutation

Quickstart

npm install -g @codewithjuber/forgekit +

${esc(d.name)} · v${esc(d.version)} · Node ${esc(d.node)}

Live status, straight from the repository.

${esc(d.description)}

Install in 60 seconds Read the docs

${esc(d.license)} license${esc(d.deps)} runtime dependencies${esc(d.branch)} @ ${esc(d.commit)}${live}
${esc(d.impact)}
blast-radius lookup

Measured from this repo's benchmark report, not a marketing placeholder.

reports/benchmarks.md

${esc(d.speed)}
pre-action gate

Assumptions, routing, reuse, context, impact, scope, and anchoring.

reports/benchmarks.md

${esc(d.saved.match(/^[\d.]+\s*%?/)?.[0] ?? d.saved)}
${esc(d.saved.replace(/^[\d.]+\s*%?\s*/, "") || "routing signal")}

Held-out result: on 80 pre-registered tasks the white-paper router spent this much more than always-premium; its 62.1% demo saving is refuted.

research/empirical-refutation

Quickstart

npm install -g @codewithjuber/forgekit forge init forge doctor forge substrate "Change auth validation and update tests"

Latest repo changes

    ${d.latest.map((x) => `
  • ${esc(x)}
  • `).join("")}

Benchmark sections indexed: ${esc(d.benchMentions)} · benchmarks file updated ${esc(d.benchUpdated)}.

Data Sources

No mock data is used. This page is regenerated from repository files during CI (generated ${esc(d.generated)} from ${esc(d.commit)}). Enable BUILD_PAGES_LIVE=1 to refresh public GitHub counters with ETag/Last-Modified caching.

  • package.json
  • README.md
  • CHANGELOG.md
  • reports/benchmarks.md
  • ${api} (optional, no auth, only when BUILD_PAGES_LIVE=1)
WCAG-minded semantic HTML, keyboard focus, responsive 320px–1920px+, and reduced-motion-safe. Same color and font tokens as the landing page — parity enforced in test/pages.test.js.
`; diff --git a/scripts/diagrams.mjs b/scripts/diagrams.mjs new file mode 100644 index 00000000..a3c0ab41 --- /dev/null +++ b/scripts/diagrams.mjs @@ -0,0 +1,445 @@ +#!/usr/bin/env node +/** + * The project's diagrams, rendered by Archify (https://github.com/tt-a1i/archify, MIT) from + * typed JSON sources — node stdlib + git only, like the rest of scripts/. + * + * docs/diagrams/src/..json the typed sources (the only hand-edited diagram files) + * docs/diagrams/.svg the dual-theme static export the docs embed + * mintlify/images/diagrams/.svg byte-identical copies for the docs site, which can + * only serve files under mintlify/ (git stores the + * identical blob once) + * docs/diagrams/diagrams.json the manifest: each diagram's id, type, title, the docs + * that embed it, and the receipt of its last verified + * render (source + artifact sha256, the archify pin) + * + * The interactive HTML (~0.8 MB per diagram, byte-deterministic for a given source and archify + * commit) is not committed: the Pages build renders it from the pinned commit and refuses to + * publish any file whose sha256 differs from its receipt, so the site serves exactly what was + * validated here. + * + * Usage: + * node scripts/diagrams.mjs check offline: sources match their receipts, + * every source is registered, SVGs present + * node scripts/diagrams.mjs validate [id…] archify showcase validation + * node scripts/diagrams.mjs build [id…] [--no-svg] validate + render + re-export the SVG and + * rewrite the receipts (after editing a source) + * node scripts/diagrams.mjs sync offline: refresh the docs-site SVG copies + * node scripts/diagrams.mjs site Pages: render every HTML (verified against + * its receipt), copy the SVGs, write a gallery + * + * Environment: FORGE_ARCHIFY_DIR — an existing archify checkout at the pinned commit (skips + * the fetch); ARCHIFY_CHROME — Chrome/Chromium for the SVG export (else archify's own lookup). + * + * Exit codes: 0 ok · 1 a check, validation or render failed · 2 usage error. + */ +import { execFileSync } from "node:child_process"; +import { createHash } from "node:crypto"; +import { + copyFileSync, + existsSync, + mkdirSync, + mkdtempSync, + readdirSync, + readFileSync, + rmSync, + writeFileSync, +} from "node:fs"; +import { tmpdir } from "node:os"; +import path from "node:path"; +import { fileURLToPath, pathToFileURL } from "node:url"; + +/** The archify release every receipt was produced with. Bumping it means re-running + * `build` for every diagram: the artifact hashes change with the renderer. */ +export const ARCHIFY = Object.freeze({ + repo: "https://github.com/tt-a1i/archify", + commit: "9e35d2b0b39b155553ba9fcfe0b4f2a5198dd993", +}); + +export const MANIFEST_PATH = "docs/diagrams/diagrams.json"; +export const SOURCE_DIR = "docs/diagrams/src"; +export const TYPES = ["architecture", "workflow", "sequence", "dataflow", "lifecycle"]; + +const DEFAULT_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); +const sha256 = (buf) => createHash("sha256").update(buf).digest("hex"); + +/** `..json` → {id, type}; null for anything else. */ +export function parseSourceName(file) { + const m = /^([a-z0-9][a-z0-9-]*)\.([a-z]+)\.json$/.exec(path.basename(file)); + return m && TYPES.includes(m[2]) ? { id: m[1], type: m[2] } : null; +} + +export function sourcePath(entry) { + return `${SOURCE_DIR}/${entry.id}.${entry.type}.json`; +} +export function svgPath(entry) { + return `docs/diagrams/${entry.id}.svg`; +} +export const MINTLIFY_SVG_DIR = "mintlify/images/diagrams"; +export function mintlifySvgPath(entry) { + return `${MINTLIFY_SVG_DIR}/${entry.id}.svg`; +} +/** The docs site embeds a diagram only through its copy under mintlify/. */ +const onDocsSite = (entry) => (entry.usedIn ?? []).some((rel) => rel.startsWith("mintlify/")); + +/** @param {string} root */ +export function readManifest(root) { + return JSON.parse(readFileSync(path.join(root, MANIFEST_PATH), "utf8")); +} + +/** + * Offline consistency check (no archify, no network): every source is registered and still + * byte-identical to the one its receipt was rendered from, and its SVG exists. + * @param {string} root + * @returns {string[]} problems; empty means consistent + */ +export function checkDiagrams(root) { + const problems = []; + let manifest; + try { + manifest = readManifest(root); + } catch (e) { + return [`${MANIFEST_PATH}: ${/** @type {Error} */ (e).message}`]; + } + if (manifest.archify?.commit !== ARCHIFY.commit) + problems.push( + `${MANIFEST_PATH}: receipts were rendered with archify ${manifest.archify?.commit ?? "?"}, scripts/diagrams.mjs pins ${ARCHIFY.commit} — run \`node scripts/diagrams.mjs build\``, + ); + const seen = new Set(); + for (const entry of manifest.diagrams ?? []) { + const where = `diagram "${entry.id}"`; + if (seen.has(entry.id)) problems.push(`${where}: duplicate id`); + seen.add(entry.id); + if (!TYPES.includes(entry.type)) problems.push(`${where}: unknown type "${entry.type}"`); + const src = path.join(root, sourcePath(entry)); + if (!existsSync(src)) { + problems.push(`${where}: source ${sourcePath(entry)} is missing`); + continue; + } + if (sha256(readFileSync(src)) !== entry.receipt?.specSha256) + problems.push( + `${where}: ${sourcePath(entry)} changed since its last verified render — run \`node scripts/diagrams.mjs build ${entry.id}\``, + ); + const svg = path.join(root, svgPath(entry)); + if (!existsSync(svg)) + problems.push( + `${where}: ${svgPath(entry)} is missing — run \`node scripts/diagrams.mjs build ${entry.id}\``, + ); + else if (onDocsSite(entry)) { + const copy = path.join(root, mintlifySvgPath(entry)); + if (!existsSync(copy) || !readFileSync(copy).equals(readFileSync(svg))) + problems.push( + `${where}: ${mintlifySvgPath(entry)} is not a byte-identical copy of ${svgPath(entry)} — run \`node scripts/diagrams.mjs sync\``, + ); + } + if (!/^[0-9a-f]{64}$/.test(entry.receipt?.artifactSha256 ?? "")) + problems.push(`${where}: receipt has no artifact sha256`); + for (const rel of entry.usedIn ?? []) { + let text = ""; + try { + text = readFileSync(path.join(root, rel), "utf8"); + } catch { + problems.push(`${where}: usedIn names ${rel}, which does not exist`); + continue; + } + if (!text.includes(`diagrams/${entry.id}.`)) + problems.push(`${where}: usedIn names ${rel}, which does not embed it`); + } + } + const dir = path.join(root, SOURCE_DIR); + for (const f of existsSync(dir) ? readdirSync(dir) : []) { + const parsed = parseSourceName(f); + if (!parsed) problems.push(`${SOURCE_DIR}/${f}: not a ..json diagram source`); + else if (!seen.has(parsed.id)) + problems.push(`${SOURCE_DIR}/${f}: not registered in ${MANIFEST_PATH}`); + } + const wanted = new Set((manifest.diagrams ?? []).filter(onDocsSite).map((e) => `${e.id}.svg`)); + const copies = path.join(root, MINTLIFY_SVG_DIR); + for (const f of existsSync(copies) ? readdirSync(copies) : []) + if (!wanted.has(f)) + problems.push( + `${MINTLIFY_SVG_DIR}/${f}: no docs-site page embeds it — run \`node scripts/diagrams.mjs sync\``, + ); + return problems; +} + +/** Make mintlify/images/diagrams/ hold exactly the SVGs the docs site embeds. */ +export function syncDocsSiteCopies(root, manifest) { + const dir = path.join(root, MINTLIFY_SVG_DIR); + const wanted = (manifest.diagrams ?? []).filter(onDocsSite); + if (wanted.length) mkdirSync(dir, { recursive: true }); + const keep = new Set(wanted.map((e) => `${e.id}.svg`)); + for (const f of existsSync(dir) ? readdirSync(dir) : []) + if (!keep.has(f)) rmSync(path.join(dir, f)); + for (const e of wanted) + copyFileSync(path.join(root, svgPath(e)), path.join(root, mintlifySvgPath(e))); +} + +// --- archify ------------------------------------------------------------------------------- + +const git = (args, cwd) => + execFileSync("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }); + +/** The pinned archify checkout: FORGE_ARCHIFY_DIR, else fetched once into .cache/. */ +export function ensureArchify(root) { + const override = process.env.FORGE_ARCHIFY_DIR; + const dir = override + ? path.resolve(override) + : path.join(root, ".cache", "archify", ARCHIFY.commit); + const cli = path.join(dir, "archify", "bin", "archify.mjs"); + if (!existsSync(cli)) { + if (override) throw new Error(`FORGE_ARCHIFY_DIR=${dir} has no archify/bin/archify.mjs`); + rmSync(dir, { recursive: true, force: true }); + mkdirSync(dir, { recursive: true }); + git(["init", "-q"], dir); + git(["fetch", "-q", "--depth", "1", ARCHIFY.repo, ARCHIFY.commit], dir); + git(["checkout", "-q", "--detach", "FETCH_HEAD"], dir); + } + const head = git(["rev-parse", "HEAD"], dir).trim(); + if (head !== ARCHIFY.commit) + throw new Error(`archify at ${dir} is ${head}, not the pinned ${ARCHIFY.commit}`); + return { dir, cli }; +} + +/** Run one archify command that prints a JSON receipt; a non-zero exit is a failure. */ +function archify(cli, args) { + try { + const out = execFileSync(process.execPath, [cli, ...args, "--json"], { + cwd: path.dirname(path.dirname(cli)), + encoding: "utf8", + stdio: ["ignore", "pipe", "pipe"], + maxBuffer: 64 * 1024 * 1024, + }); + return JSON.parse(out); + } catch (e) { + const err = /** @type {any} */ (e); + let detail = String(err.stdout || err.stderr || err.message).trim(); + try { + const j = JSON.parse(err.stdout); + detail = JSON.stringify(j.diagnostics ?? j.errors ?? j, null, 2).slice(0, 4000); + } catch {} + throw new Error(`archify ${args.slice(0, 2).join(" ")} failed:\n${detail}`); + } +} + +/** Validate, then render one diagram to `out`; returns the delivery receipt. */ +export function render(root, cli, entry, out) { + const src = path.join(root, sourcePath(entry)); + archify(cli, ["validate", entry.type, src, "--quality", "showcase"]); + const r = archify(cli, ["deliver", entry.type, src, out, "--quality", "showcase"]); + if (!r.ok) throw new Error(`archify deliver ${entry.id}: not ok`); + return r; +} + +/** Export the dual-theme SVG through the viewer's own Export menu, headless (archify's + * pipe-CDP Chrome driver — no browser automation dependency). */ +async function exportSvg(archifyDir, html, target) { + const vc = await import( + pathToFileURL(path.join(archifyDir, "archify", "bin", "visual-check.mjs")).href + ); + const chrome = process.env.ARCHIFY_CHROME || vc.findChrome(); + if (!chrome) throw new Error("no Chrome/Chromium found — set ARCHIFY_CHROME"); + const downloads = mkdtempSync(path.join(tmpdir(), "forge-diagram-svg-")); + const browser = new vc.ChromeVisualBrowser(chrome); + try { + const sid = await browser.sessionPromise; + const send = (method, params) => browser.cdp.send(method, params, sid); + await browser.cdp.send("Browser.setDownloadBehavior", { + behavior: "allow", + downloadPath: downloads, + eventsEnabled: true, + }); + await send("Emulation.setDeviceMetricsOverride", { + width: 1600, + height: 1000, + deviceScaleFactor: 1, + mobile: false, + }); + await send("Page.navigate", { url: pathToFileURL(html).href }); + const evaluate = async (expression) => + (await send("Runtime.evaluate", { expression, awaitPromise: true, returnByValue: true })) + .result?.value; + for (let i = 0; i < 100 && !(await evaluate("!!document.getElementById('btn-export')")); i++) + await new Promise((r) => setTimeout(r, 100)); + const clicked = await evaluate(`(() => { + document.getElementById('btn-export')?.click(); + const b = [...document.querySelectorAll('[data-format="svg"]')].find((e) => !e.dataset.variant); + if (!b) return false; + b.click(); + return true; + })()`); + if (!clicked) throw new Error(`${html}: the viewer has no SVG export control`); + let file; + for (let i = 0; i < 150 && !file; i++) { + await new Promise((r) => setTimeout(r, 100)); + file = readdirSync(downloads).find((f) => f.endsWith(".svg")); + } + if (!file) throw new Error(`${html}: the SVG export did not arrive`); + copyFileSync(path.join(downloads, file), target); + } finally { + await browser.close(); + rmSync(downloads, { recursive: true, force: true }); + } +} + +// --- site ---------------------------------------------------------------------------------- + +const esc = (s) => + String(s ?? "").replace( + /[&<>"]/g, + (c) => + /** @type {Record} */ ({ + "&": "&", + "<": "<", + ">": ">", + '"': """, + })[c], + ); + +const DESCRIPTION = + "Interactive diagrams of how forgekit works, rendered by Archify from typed sources."; + +/** The /diagrams/ gallery page: one card per diagram, the SVG as its preview. */ +export function galleryHtml(manifest, { site = "", repo = "", tokensCss = "" } = {}) { + const cards = manifest.diagrams + .map( + (d) => + `
  • ${esc(d.title)}${esc(d.type)}
  • `, + ) + .join(""); + return `forgekit diagrams
    +

    How forgekit works

    Each diagram opens as an interactive page: pan and zoom, search with /, press ? for the guide, and trace a node's upstream and downstream relationships. They are rendered by Archify from typed sources in docs/diagrams, and each file is checked against the receipt of its validated render.

    +
      ${cards}
    + +
    +`; +} + +/** Render every diagram into `outDir` for the Pages site, verified against its receipt. */ +export async function buildSite(root, outDir) { + const problems = checkDiagrams(root); + if (problems.length) throw new Error(problems.join("\n")); + const manifest = readManifest(root); + const { cli } = ensureArchify(root); + mkdirSync(outDir, { recursive: true }); + for (const entry of manifest.diagrams) { + const out = path.join(outDir, `${entry.id}.html`); + const r = render(root, cli, entry, out); + if (r.artifact?.sha256 !== entry.receipt.artifactSha256) + throw new Error( + `diagram "${entry.id}": rendered ${r.artifact?.sha256}, receipt says ${entry.receipt.artifactSha256} — refusing to publish an artifact that differs from the validated one`, + ); + copyFileSync(path.join(root, svgPath(entry)), path.join(outDir, `${entry.id}.svg`)); + } + const { BRAND, rootTokensCss } = await import("../src/brand.js"); + const { stripTrailingSlashes } = await import("../src/util.js"); + const trim = (u) => stripTrailingSlashes(String(u ?? "")); + writeFileSync( + path.join(outDir, "index.html"), + galleryHtml(manifest, { + site: trim(BRAND.site?.url), + repo: trim(BRAND.site?.repo), + tokensCss: rootTokensCss(), + }), + ); + return manifest.diagrams.length; +} + +// --- CLI ----------------------------------------------------------------------------------- + +async function main(argv) { + const [cmd, ...rest] = argv; + const root = DEFAULT_ROOT; + const flags = new Set(rest.filter((a) => a.startsWith("--"))); + const ids = rest.filter((a) => !a.startsWith("--")); + if (cmd === "check") { + const problems = checkDiagrams(root); + for (const p of problems) process.stderr.write(`${p}\n`); + if (!problems.length) { + const n = readManifest(root).diagrams.length; + process.stdout.write(`ok: ${n} diagrams match their receipts\n`); + } + return problems.length ? 1 : 0; + } + if (cmd === "sync") { + syncDocsSiteCopies(root, readManifest(root)); + process.stdout.write(`synced ${MINTLIFY_SVG_DIR}/\n`); + return 0; + } + if (cmd === "site") { + if (!ids[0]) { + process.stderr.write("usage: node scripts/diagrams.mjs site \n"); + return 2; + } + const n = await buildSite(root, path.resolve(ids[0])); + process.stdout.write(`rendered ${n} diagrams into ${ids[0]}\n`); + return 0; + } + if (cmd === "validate" || cmd === "build") { + const manifest = readManifest(root); + const pick = ids.length + ? manifest.diagrams.filter((d) => ids.includes(d.id)) + : manifest.diagrams; + const unknown = ids.filter((i) => !manifest.diagrams.some((d) => d.id === i)); + if (unknown.length) { + process.stderr.write(`unknown diagram id(s): ${unknown.join(", ")}\n`); + return 2; + } + const { dir, cli } = ensureArchify(root); + const work = mkdtempSync(path.join(tmpdir(), "forge-diagrams-")); + try { + for (const entry of pick) { + const src = path.join(root, sourcePath(entry)); + if (cmd === "validate") { + const v = archify(cli, ["validate", entry.type, src, "--quality", "showcase"]); + process.stdout.write( + `ok ${entry.id} ${JSON.stringify(v.summary ?? v.validation ?? "valid")}\n`, + ); + continue; + } + const html = path.join(work, `${entry.id}.html`); + const r = render(root, cli, entry, html); + entry.title = JSON.parse(readFileSync(src, "utf8")).meta?.title ?? entry.id; + if (!flags.has("--no-svg")) await exportSvg(dir, html, path.join(root, svgPath(entry))); + entry.receipt = { + specSha256: r.specification.sha256, + artifactSha256: r.artifact.sha256, + artifactBytes: r.artifact.bytes, + checks: + `${r.validation?.checksPassed}/${r.validation?.checkCount} ${r.validation?.compositionProfile ?? ""}`.trim(), + }; + process.stdout.write( + `ok ${entry.id} ${entry.receipt.checks} ${entry.receipt.artifactSha256.slice(0, 12)}\n`, + ); + } + } finally { + rmSync(work, { recursive: true, force: true }); + } + if (cmd === "build") { + manifest.archify = { ...ARCHIFY, license: "MIT" }; + writeFileSync(path.join(root, MANIFEST_PATH), `${JSON.stringify(manifest, null, 2)}\n`); + syncDocsSiteCopies(root, manifest); + } + return 0; + } + process.stderr.write( + "usage: node scripts/diagrams.mjs check | validate [id…] | build [id…] [--no-svg] | sync | site \n", + ); + return 2; +} + +if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { + main(process.argv.slice(2)).then( + (code) => process.exit(code), + (e) => { + process.stderr.write(`${e.message}\n`); + process.exit(1); + }, + ); +} diff --git a/scripts/og-card.mjs b/scripts/og-card.mjs new file mode 100644 index 00000000..9a8027fb --- /dev/null +++ b/scripts/og-card.mjs @@ -0,0 +1,124 @@ +#!/usr/bin/env node +/** + * Render the social card: docs/assets/og.svg (type over docs/assets/og-background.webp) + * → docs/assets/og.jpg at 2× (2400×1260 for the declared 1200×630). The Pages build copies + * og.jpg to the site root, where og:image and twitter:image point. JPEG, not PNG: the art is + * photographic, and link-preview scrapers (WhatsApp among them) skip images over ~300 KB. + * + * Uses the Chrome driver of the pinned Archify checkout (scripts/diagrams.mjs), so there is + * no browser-automation dependency. It refuses to write a card whose fonts did not load or + * whose text leaves the art's empty left side (x ≤ 610 of 1200) or the crop-safe area. + * + * node scripts/og-card.mjs # render docs/assets/og.jpg + * node scripts/og-card.mjs --check # measure only, write nothing + * + * Environment: ARCHIFY_CHROME (Chrome/Chromium), FORGE_ARCHIFY_DIR (see scripts/diagrams.mjs). + * Needs network once for the Inter and JetBrains Mono web fonts (Google Fonts, OFL). + */ +import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import path from "node:path"; +import { fileURLToPath, pathToFileURL } from "node:url"; +import { ensureArchify } from "./diagrams.mjs"; + +const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); +const assets = path.join(root, "docs", "assets"); +/** Where the art begins on the right, and the crop-safe margins (1200×630 units). */ +export const SAFE = Object.freeze({ left: 60, top: 36, right: 610, bottom: 594 }); +/** Link-preview scrapers skip larger images (WhatsApp at about 300 KB). */ +const MAX_BYTES = 300 * 1024; + +/** Problems with measured text boxes; empty means every line sits in the safe region. */ +export function layoutProblems(boxes, safe = SAFE) { + return boxes + .filter( + (b) => b.x < safe.left || b.y < safe.top || b.x + b.w > safe.right || b.y + b.h > safe.bottom, + ) + .map( + (b) => + `"${b.text}" spans x ${b.x.toFixed(0)}–${(b.x + b.w).toFixed(0)}, y ${b.y.toFixed(0)}–${(b.y + b.h).toFixed(0)}; allowed x ${safe.left}–${safe.right}, y ${safe.top}–${safe.bottom}`, + ); +} + +async function main(argv) { + const check = argv.includes("--check"); + const { dir } = ensureArchify(root); + const vc = await import(pathToFileURL(path.join(dir, "archify", "bin", "visual-check.mjs")).href); + const chrome = process.env.ARCHIFY_CHROME || vc.findChrome(); + if (!chrome) throw new Error("no Chrome/Chromium found — set ARCHIFY_CHROME"); + const svg = readFileSync(path.join(assets, "og.svg"), "utf8"); + const work = mkdtempSync(path.join(tmpdir(), "forge-og-")); + const page = path.join(work, "og.html"); + writeFileSync( + page, + ` + +${svg}`, + ); + const browser = new vc.ChromeVisualBrowser(chrome); + try { + const sid = await browser.sessionPromise; + const send = (method, params) => browser.cdp.send(method, params, sid, 60000); + await send("Emulation.setDeviceMetricsOverride", { + width: 1200, + height: 630, + deviceScaleFactor: 2, + mobile: false, + }); + await send("Page.navigate", { url: pathToFileURL(page).href }); + const evaluate = async (expression) => + (await send("Runtime.evaluate", { expression, awaitPromise: true, returnByValue: true })) + .result?.value; + const ready = await evaluate(`(async () => { + for (let i = 0; i < 200 && document.readyState !== "complete"; i++) await new Promise((r) => setTimeout(r, 50)); + await document.fonts.ready; + const art = new Image(); art.src = "og-background.webp"; + try { await art.decode(); } catch { return { art: false }; } + return { art: art.naturalWidth > 0, inter: document.fonts.check('800 54px Inter'), mono: document.fonts.check('600 16px "JetBrains Mono"') }; + })()`); + if (!ready?.art) throw new Error("og-background.webp did not load"); + if (!ready.inter || !ready.mono) + throw new Error( + `web fonts did not load (Inter ${ready.inter}, JetBrains Mono ${ready.mono}) — the card needs network once`, + ); + const boxes = await evaluate( + `[...document.querySelectorAll('svg text')].map((t) => { const b = t.getBoundingClientRect(); return { text: t.textContent.trim(), x: b.left, y: b.top, w: b.width, h: b.height }; })`, + ); + const problems = layoutProblems(boxes); + if (problems.length) + throw new Error(`text outside the card's safe region:\n${problems.join("\n")}`); + process.stdout.write(`layout ok: ${boxes.length} lines inside x ${SAFE.left}–${SAFE.right}\n`); + if (check) return 0; + // The highest JPEG quality that keeps the card under the scrapers' ~300 KB limit. + let jpg; + for (const quality of [90, 86, 82, 78, 74]) { + const shot = await send("Page.captureScreenshot", { + format: "jpeg", + quality, + clip: { x: 0, y: 0, width: 1200, height: 630, scale: 1 }, + }); + jpg = { quality, bytes: Buffer.from(shot.data, "base64") }; + if (jpg.bytes.length <= MAX_BYTES) break; + } + if (jpg.bytes.length > MAX_BYTES) + throw new Error(`og.jpg is ${jpg.bytes.length} bytes even at quality ${jpg.quality}`); + writeFileSync(path.join(assets, "og.jpg"), jpg.bytes); + process.stdout.write( + `wrote docs/assets/og.jpg (2400×1260, quality ${jpg.quality}, ${jpg.bytes.length} bytes)\n`, + ); + return 0; + } finally { + await browser.close(); + rmSync(work, { recursive: true, force: true }); + } +} + +if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { + main(process.argv.slice(2)).then( + (code) => process.exit(code), + (e) => { + process.stderr.write(`${e.message}\n`); + process.exit(1); + }, + ); +} diff --git a/src/docs_check.js b/src/docs_check.js index 09e39e35..b52de875 100644 --- a/src/docs_check.js +++ b/src/docs_check.js @@ -219,6 +219,15 @@ const MERMAID_BLOCK_RE = /```mermaid\n([\s\S]*?)```/g; * "the diagrams look bad" from silently recurring; nothing else reconciled diagram quality. */ function checkDiagrams(root, issues) { + // A repo that renders its diagrams with Archify (a docs/diagrams/diagrams.json manifest) + // draws them from typed, validated sources: an embed must name a registered diagram, and + // a hand-written Mermaid block is a diagram that skipped that pipeline. Machine-owned + // blocks (`forge:render` markers) and opted-out examples stay allowed. + let archify = null; + try { + const manifest = JSON.parse(readFileSync(join(root, "docs/diagrams/diagrams.json"), "utf8")); + archify = new Set((manifest.diagrams ?? []).map((d) => d.id)); + } catch {} for (const rel of markdownFiles(root)) { let text; try { @@ -226,10 +235,31 @@ function checkDiagrams(root, issues) { } catch { continue; } + if (archify) { + for (const m of text.matchAll(/diagrams\/([a-z0-9][a-z0-9-]*)\.(?:svg|html)\b/g)) { + if (!archify.has(m[1])) + issues.push({ + check: "diagrams", + severity: "error", + detail: `${rel}: embeds diagram "${m[1]}", which is not registered in docs/diagrams/diagrams.json`, + }); + } + } for (const m of text.matchAll(MERMAID_BLOCK_RE)) { // An intentional example block (e.g. docs showing what a BAD diagram looks like) opts // out with an HTML comment `` on the line before the fence. if (/docs-check-ignore/.test(text.slice(Math.max(0, m.index - 80), m.index))) continue; + // Machine-owned: inside a `forge:render` block whose end marker has not been reached. + const before = text.slice(0, m.index); + const generated = + before.lastIndexOf("forge:render:") !== -1 && + /forge:render:[a-z-]+:begin/.test(before.slice(before.lastIndexOf("forge:render:"))); + if (archify && !generated) + issues.push({ + check: "diagrams", + severity: "error", + detail: `${rel}: a hand-written mermaid diagram — draw it as an Archify source in docs/diagrams/src and embed its SVG (see docs/diagrams/README.md)`, + }); const block = m[1]; if (!block.includes("%%{init")) { issues.push({ diff --git a/src/util.js b/src/util.js index 4a7991ec..66ad0486 100644 --- a/src/util.js +++ b/src/util.js @@ -139,6 +139,9 @@ export const IGNORE_DIRS = new Set([ "coverage", ".venv", "vendor", + // Build caches — e.g. the pinned Archify checkout scripts/diagrams.mjs fetches, which + // would otherwise enter the import graph and the generated repo map. + ".cache", ]); export const SRC_EXT = /\.(js|jsx|ts|tsx|mjs|cjs|py)$/; diff --git a/test/diagrams.test.js b/test/diagrams.test.js new file mode 100644 index 00000000..5a0454df --- /dev/null +++ b/test/diagrams.test.js @@ -0,0 +1,162 @@ +// The Archify diagram pipeline (scripts/diagrams.mjs): the offline receipt check, source +// naming, the gallery page, and the docs-check rules for embeds and hand-written Mermaid. +import assert from "node:assert/strict"; +import { createHash } from "node:crypto"; +import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { dirname, join } from "node:path"; +import { test } from "node:test"; +import { + ARCHIFY, + checkDiagrams, + galleryHtml, + MANIFEST_PATH, + parseSourceName, + syncDocsSiteCopies, +} from "../scripts/diagrams.mjs"; +import { BRAND } from "../src/brand.js"; +import { docsCheck } from "../src/docs_check.js"; + +const sha = (s) => createHash("sha256").update(s).digest("hex"); + +function fixture() { + const root = mkdtempSync(join(tmpdir(), "forge-diagrams-")); + const write = (rel, text) => { + mkdirSync(dirname(join(root, rel)), { recursive: true }); + writeFileSync(join(root, rel), text); + }; + const source = '{"meta":{"title":"A flow"}}\n'; + write("docs/diagrams/src/flow.workflow.json", source); + write("docs/diagrams/flow.svg", ""); + write("README.md", "![flow](docs/diagrams/flow.svg)\n"); + const manifest = { + archify: { ...ARCHIFY, license: "MIT" }, + diagrams: [ + { + id: "flow", + type: "workflow", + title: "A flow", + usedIn: ["README.md"], + receipt: { specSha256: sha(source), artifactSha256: "a".repeat(64) }, + }, + ], + }; + write(MANIFEST_PATH, JSON.stringify(manifest)); + return { root, write, manifest, cleanup: () => rmSync(root, { recursive: true, force: true }) }; +} + +test("parseSourceName accepts ..json for the five archify types only", () => { + assert.deepEqual(parseSourceName("docs/diagrams/src/core-loop.workflow.json"), { + id: "core-loop", + type: "workflow", + }); + assert.equal(parseSourceName("x.flowchart.json"), null); + assert.equal(parseSourceName("Bad Name.workflow.json"), null); + assert.equal(parseSourceName("notes.md"), null); +}); + +test("checkDiagrams: consistent fixture passes; edits, strays and broken embeds are named", () => { + const f = fixture(); + try { + assert.deepEqual(checkDiagrams(f.root), []); + f.write("docs/diagrams/src/flow.workflow.json", '{"meta":{"title":"edited"}}\n'); + assert.ok(checkDiagrams(f.root).some((p) => /changed since its last verified render/.test(p))); + f.write("docs/diagrams/src/stray.sequence.json", "{}"); + assert.ok(checkDiagrams(f.root).some((p) => /stray\.sequence\.json: not registered/.test(p))); + f.write("README.md", "no diagram here\n"); + assert.ok( + checkDiagrams(f.root).some((p) => /usedIn names README\.md, which does not embed/.test(p)), + ); + rmSync(join(f.root, "docs/diagrams/flow.svg")); + assert.ok(checkDiagrams(f.root).some((p) => /flow\.svg is missing/.test(p))); + } finally { + f.cleanup(); + } +}); + +test("checkDiagrams: receipts from another archify commit are stale", () => { + const f = fixture(); + try { + f.write( + MANIFEST_PATH, + JSON.stringify({ ...f.manifest, archify: { ...ARCHIFY, commit: "0".repeat(40) } }), + ); + assert.ok(checkDiagrams(f.root).some((p) => /rendered with archify 0{40}/.test(p))); + } finally { + f.cleanup(); + } +}); + +test("checkDiagrams: the docs site's SVG copies must match, and sync repairs them", () => { + const f = fixture(); + try { + f.write("mintlify/page.mdx", '\n'); + f.manifest.diagrams[0].usedIn.push("mintlify/page.mdx"); + f.write(MANIFEST_PATH, JSON.stringify(f.manifest)); + assert.ok( + checkDiagrams(f.root).some((p) => + /images\/diagrams\/flow\.svg is not a byte-identical/.test(p), + ), + ); + f.write("mintlify/images/diagrams/flow.svg", "stale"); + f.write("mintlify/images/diagrams/old.svg", ""); + const problems = checkDiagrams(f.root); + assert.ok(problems.some((p) => /flow\.svg is not a byte-identical/.test(p))); + assert.ok(problems.some((p) => /old\.svg: no docs-site page embeds it/.test(p))); + syncDocsSiteCopies(f.root, f.manifest); + assert.deepEqual(checkDiagrams(f.root), []); + assert.equal(readFileSync(join(f.root, "mintlify/images/diagrams/flow.svg"), "utf8"), ""); + } finally { + f.cleanup(); + } +}); + +test("galleryHtml lists every diagram, escaped, with its SVG preview and interactive page", () => { + const html = galleryHtml( + { diagrams: [{ id: "flow", type: "workflow", title: "A & more" }] }, + { site: "https://example.test/site", repo: "https://example.test/owner/repo" }, + ); + assert.match(html, / { + const f = fixture(); + try { + f.write( + "docs/page.md", + "![x](diagrams/unknown.svg)\n\n```mermaid\nflowchart LR\n A --> B\n```\n", + ); + const r = docsCheck({ root: f.root }); + const details = r.issues.filter((i) => i.check === "diagrams").map((i) => i.detail); + assert.ok( + details.some((d) => /embeds diagram "unknown"/.test(d)), + details.join("\n"), + ); + assert.ok( + details.some((d) => /hand-written mermaid diagram/.test(d)), + details.join("\n"), + ); + // A machine-owned block (inside forge:render markers) is still allowed. + f.write( + "docs/page.md", + "\n```mermaid\nflowchart LR\n A --> B\n```\n\n", + ); + const again = docsCheck({ root: f.root }).issues.filter( + (i) => i.check === "diagrams" && /hand-written/.test(i.detail), + ); + assert.deepEqual(again, []); + } finally { + f.cleanup(); + } +}); + +test("the repository's diagrams match their receipts and are embedded where the manifest says", () => { + assert.deepEqual(checkDiagrams(BRAND.root), []); + const manifest = JSON.parse(readFileSync(join(BRAND.root, MANIFEST_PATH), "utf8")); + assert.ok(manifest.diagrams.length >= 13); + for (const d of manifest.diagrams) + assert.ok(d.usedIn.length >= 1, `${d.id} is embedded somewhere`); +}); diff --git a/test/docs_render.test.js b/test/docs_render.test.js index 0c73d632..9f051027 100644 --- a/test/docs_render.test.js +++ b/test/docs_render.test.js @@ -92,6 +92,10 @@ test("renderRepoMap draws directories and import edges from the real tree", () = assert.ok(map.includes(mermaidInit()), "uses the shared brand theme"); assert.ok(map.includes('app["app
    1 file"]') && map.includes('lib["lib
    1 file"]')); assert.ok(map.includes("app --> lib"), "the import edge points importer → imported"); + // A build cache (e.g. a fetched tool checkout under .cache/) is not part of the repo. + mkdirSync(join(root, ".cache", "tool"), { recursive: true }); + writeFileSync(join(root, ".cache", "tool", "x.mjs"), 'import "./y.mjs";\n'); + assert.equal(renderRepoMap(root), map, ".cache/ never enters the map"); }); test("renderDocs fills managed blocks, is idempotent, and reports tampering as strict drift", () => { diff --git a/test/pages.test.js b/test/pages.test.js index b6294fab..176ca750 100644 --- a/test/pages.test.js +++ b/test/pages.test.js @@ -110,16 +110,19 @@ test("both public pages ship social image + favicon (no blank cards)", async () ["landing", landing], ["status", status], ]) { - assert.match( - html, - /property="og:image"[^>]*content="https:\/\/[^"]+\.png"/, - `${name}: absolute og:image`, - ); - assert.match( - html, - /name="twitter:image"[^>]*content="https:\/\/[^"]+\.png"/, - `${name}: twitter:image`, + const og = html.match( + /property="og:image"[^>]*content="(https:\/\/[^"]+\.(?:png|jpe?g))"/, + )?.[1]; + assert.ok(og, `${name}: absolute og:image`); + // The Pages build publishes the card from docs/assets; a URL naming a file that is not + // there is a blank card in every link preview. + const card = og.split("/").pop(); + assert.ok( + existsSync(fileURLToPath(new URL(`../docs/assets/${card}`, import.meta.url))), + `${name}: og:image ${card} ships from docs/assets`, ); + const twitter = html.match(/name="twitter:image"[^>]*content="(https:\/\/[^"]+)"/)?.[1]; + assert.equal(twitter?.split("/").pop(), card, `${name}: twitter:image is the same card`); assert.match(html, /rel="icon"[^>]*image\/svg/, `${name}: svg favicon`); assert.match(html, /rel="apple-touch-icon"/, `${name}: apple-touch-icon`); }