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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 39 additions & 11 deletions .github/workflows/bump.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
# One-click release kickoff: Actions -> "Bump version" -> Run workflow.
# Runs the tests, bumps every version field via scripts/bump.mjs (package.json,
# package-lock.json, both plugin manifests, CITATION.cff, landing page, CHANGELOG
# rotation), commits "chore(release): vX.Y.Z", tags vX.Y.Z, and pushes commit + tag.
# Auto-release. Two ways in:
# 1. Merge to master (push) -> "auto" bump from conventional commits since the last tag.
# A chore/docs-only merge is a graceful skip (bump.mjs exits 3), so nothing publishes
# unless a feat/fix/perf/breaking change actually landed.
# 2. Actions -> "Bump version" -> Run workflow -> pick auto/patch/minor/major by hand.
#
# Pushing the v* tag is what normally triggers release.yml — but pushes made with
# the default GITHUB_TOKEN deliberately do NOT trigger other workflows (GitHub's
# recursion guard). So this workflow also dispatches release.yml on the new tag
# explicitly, which needs `actions: write` (granted below). No PAT required.
# Either way scripts/bump.mjs bumps every version field (package.json, package-lock.json,
# both plugin manifests, CITATION.cff, landing page) + rotates CHANGELOG, then this workflow
# commits "chore(release): vX.Y.Z", tags vX.Y.Z, and pushes commit + tag.
#
# Pushing the v* tag is what normally triggers release.yml — but pushes made with the default
# GITHUB_TOKEN deliberately do NOT trigger other workflows (GitHub's recursion guard). So this
# workflow also dispatches release.yml on the new tag explicitly, which needs `actions: write`.
# That same recursion guard is why the release commit this workflow pushes does NOT re-trigger
# the push path below (no infinite loop); the actor/subject guard is belt-and-suspenders.
name: Bump version

on:
push:
branches: [master]
workflow_dispatch:
inputs:
bump:
Expand All @@ -33,6 +41,10 @@ concurrency:
jobs:
bump:
runs-on: ubuntu-latest
# Never react to our own release commit (belt-and-suspenders behind GitHub's recursion guard).
if: >-
github.actor != 'github-actions[bot]' &&
!startsWith(github.event.head_commit.message, 'chore(release):')
steps:
- uses: actions/checkout@v7
with:
Expand All @@ -45,12 +57,27 @@ jobs:
- run: npm test # never tag a broken tree
- name: Bump version fields + rotate CHANGELOG
id: bump
# On push, always "auto"; on manual dispatch, honor the chosen input.
env:
BUMP: ${{ inputs.bump }}
BUMP: ${{ github.event_name == 'workflow_dispatch' && inputs.bump || 'auto' }}
run: |
set +e
VERSION="$(node scripts/bump.mjs "$BUMP")"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
code=$?
set -e
if [ "$code" -eq 0 ]; then
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "release=true" >> "$GITHUB_OUTPUT"
echo "::notice::Releasing v$VERSION"
elif [ "$code" -eq 3 ]; then
echo "release=false" >> "$GITHUB_OUTPUT"
echo "::notice::Nothing to release (no feat/fix/perf/breaking commits) — skipping."
else
echo "::error::bump.mjs failed (exit $code)"
exit "$code"
fi
- name: Commit, tag, push
if: steps.bump.outputs.release == 'true'
env:
VERSION: ${{ steps.bump.outputs.version }}
run: |
Expand All @@ -59,8 +86,9 @@ jobs:
git add -A
git commit -m "chore(release): v$VERSION"
git tag -a "v$VERSION" -m "v$VERSION"
git push origin HEAD "v$VERSION"
git push origin "HEAD:${{ github.ref_name }}" "v$VERSION"
- name: Trigger the release workflow on the new tag
if: steps.bump.outputs.release == 'true'
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ steps.bump.outputs.version }}
Expand Down
46 changes: 33 additions & 13 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,19 +88,23 @@ checks and returns a single verdict. It composes the individually-callable stage

```mermaid
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#201a15','primaryTextColor':'#f2ede7','primaryBorderColor':'#372c22','lineColor':'#f26430','secondaryColor':'#272019','tertiaryColor':'#171310','fontFamily':'ui-sans-serif, system-ui, sans-serif'}}}%%
flowchart LR
RE["referenced<br/>entities"] --> PF["preflight<br/>assumption gap"]
PF --> RT["route<br/>cheapest tier"]
RT --> AT["atlas<br/>code graph"]
AT --> IM["impact<br/>blast radius"]
IM --> PT["predict<br/>failing tests"]
PT --> RU["reuse<br/>cache hit?"]
RU --> CX["context<br/>completeness gate"]
CX --> SC["scope<br/>coupled files"]
SC --> ME["memory<br/>recall + lessons"]
ME --> MN["minimality<br/>lean footprint"]
MN --> GA["goal-anchor<br/>drift check"]
GA --> VD["verdict"]
flowchart TD
RE["referenced entities"] --> INTAKE
subgraph INTAKE["intake"]
direction LR
PF["preflight<br/>assumption gap"] --> RT["route<br/>cheapest tier"]
end
INTAKE --> ANALYSIS
subgraph ANALYSIS["analysis"]
direction LR
AT["atlas<br/>code graph"] --> IM["impact<br/>blast radius"] --> PT["predict<br/>failing tests"] --> RU["reuse<br/>cache hit?"]
end
ANALYSIS --> SAFETY
subgraph SAFETY["safety + fit"]
direction LR
CX["context<br/>completeness gate"] --> SC["scope<br/>coupled files"] --> ME["memory<br/>recall + lessons"] --> MN["minimality<br/>lean footprint"] --> GA["goal-anchor<br/>drift check"]
end
SAFETY --> VD["verdict"]
classDef accent fill:#f26430,stroke:#f26430,color:#171310;
class VD accent;
```
Expand Down Expand Up @@ -226,6 +230,22 @@ called symbols, from added AND removed lines, via the same `RULES` grammars the
parses) swept against every doc artifact → UPDATED / STALE (file:line hits) /
VERIFIED-UNAFFECTED with the reason recorded. Pure reporter; the gate provides the teeth.

**Docs-check now guards more than names (`src/docs_check.js`).** Beyond
commands/env/MCP-tools/CHANGELOG, three reconcilers close the blind spots behind recurring
"docs rot" complaints: `checkDiagrams` scans every `mermaid` block across all Markdown for
the branded `%%{init` theme and literal-`\n` node breaks; `checkModelTiers` reconciles doc
prose prices against `src/model_tiers.json`; `checkBenchmarks` reconciles bolded `N ms`
README claims against the measured table in `reports/benchmarks.md`. The two public pages
(`landing/index.html` + the `build-pages.mjs` status page) share one set of design tokens,
enforced for parity (plus non-empty changes list, no phantom webfont) by
`test/pages.test.js` — so neither the docs' numbers nor the site's look can silently drift.

**Auto-release (`.github/workflows/bump.yml` + `scripts/bump.mjs`).** A push to `master`
runs `bump.mjs auto`: it releases only when a `feat`/`fix`/`perf`/breaking commit landed
(or `[Unreleased]` was hand-written), synthesizing changelog notes from commit subjects
when none exist, and exits `3` (a clean skip, not a failure) otherwise — so releases cut
themselves without a chore/docs merge spamming the registry.

**Intent cards (`src/intent.js`).** Prompt → intent by the same exemplar k-NN math as
model routing — a labeled bank (English + Hinglish rows) under overlap similarity with a
confidence gate, NOT a keyword DFA. Note `intentGrams` ≠ `contentGrams`: route.js stops
Expand Down
31 changes: 30 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,42 @@ doctor` surfaces a non-nagging "update available" notice; `FORGE_NO_UPDATE_CHECK
- **Self-dogfood** — a committed `.claude/settings.json` wires forgekit's own guards via
`${CLAUDE_PROJECT_DIR}`, so the repo runs its own completion gate, cortex, and guards
during local dev without a marketplace install.
- **Auto-release on merge** — pushing a `feat`/`fix`/`perf`/breaking change to `master`
now cuts the release automatically (bump → tag → npm publish → GitHub Release); a
chore/docs-only merge skips cleanly (`bump.mjs auto` exits `3`). When `[Unreleased]` is
empty, `bump.mjs` synthesizes the changelog body from commit subjects so every release
still describes itself. Manual **Actions → Bump version** dispatch stays available.
- **`forge docs check` now guards diagrams, model prices, and benchmark numbers** — three
new reconcilers close the blind spots behind recurring complaints: every `mermaid` block
across all Markdown must carry the branded theme and use `<br/>` (not a literal `\n`);
model prices in the docs must match `src/model_tiers.json`; and every bolded `N ms`
claim in the README must be a value `reports/benchmarks.md` actually measured.

### Changed

- **CLI output is quiet by default** — the `Forge <command> — …` title line no longer
prints on every command; results come first. `--verbose` or `FORGE_VERBOSE=1` restores
it. The `--help` / `--version` banner is unchanged.
- **Unified public design system** — the landing page and the generated status page now
share one warm ember/near-black palette and a system font stack (the landing page no
longer declares the Inter webfont it never loaded). `test/pages.test.js` enforces token
parity, a non-empty changes list, and no phantom webfont.
- **Restyled the terminal statusline** — a restrained palette (muted structure, one ember
accent, green/red reserved for the diff) with consistent `·` separators and a subtle
context-limit marker instead of an alarming red block.
- **Plain-language docs pass** — the README and GUIDE openings lead with what forgekit is
and does; the deep math (`y = f(x)`, join-semilattice, Beta posteriors) moved into
parentheticals or the white paper, and comparison-table cells no longer cite code
identifiers as if they were user features.

### Fixed

- **Broken diagrams** — two `mermaid` diagrams rendered in Mermaid's off-brand default
theme, one with literal `\n` node breaks GitHub showed as garbage; both fixed, and the
over-wide 13-node pre-action pipeline was regrouped so it reads at GitHub width.
- **Status page "Latest changes" list** — wrapped CHANGELOG bullets were truncated
mid-sentence (and could render empty); the parser now joins lazy/indented continuation
lines into the full item.

## [0.10.0] - 2026-07-10

Expand Down Expand Up @@ -619,7 +649,6 @@ consolidate` reconciles deletions into tombstones. `putClaim` repairs corrupt/tr
[0.7.0]: https://github.com/CodeWithJuber/forgekit/compare/v0.6.0...v0.7.0
[0.6.0]: https://github.com/CodeWithJuber/forgekit/compare/v0.5.0...v0.6.0
[0.5.0]: https://github.com/CodeWithJuber/forgekit/compare/v0.4.0...v0.5.0
[Unreleased]: https://github.com/CodeWithJuber/forgekit/compare/v0.4.0...HEAD
[0.4.0]: https://github.com/CodeWithJuber/forgekit/compare/v0.3.1...v0.4.0
[0.3.1]: https://github.com/CodeWithJuber/forgekit/compare/v0.3.0...v0.3.1
[0.3.0]: https://github.com/CodeWithJuber/forgekit/compare/v0.2.0...v0.3.0
Expand Down
11 changes: 8 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ npm run check # Biome lint + format check
3. Run `npm test`, `npm run typecheck`, and `npm run check:fix` before committing.
4. Use [Conventional Commits](https://www.conventionalcommits.org/):
`feat(scope): description`, `fix: …`, `docs: …`.
5. Add a line to `CHANGELOG.md` under `## [Unreleased]`.
5. Add a line to `CHANGELOG.md` under `## [Unreleased]`. This is your release note —
when your PR merges, the auto-release uses it verbatim. (Skip it and the release
still cuts, synthesizing notes from your commit subjects — but your wording is better.)
6. Open a PR. CI (tests on Node 20/22, Biome, typecheck, shellcheck) must pass.

## Project shape
Expand Down Expand Up @@ -68,8 +70,11 @@ We'd rather give a clear "not now" than merge something that adds maintenance bu

## Releasing (maintainers)

Cutting a release is one command plus a tag — the full runbook (and the one-time
`NPM_TOKEN` setup) is in [docs/RELEASING.md](./docs/RELEASING.md).
Releases are automatic: **merging a `feat`/`fix`/`perf`/breaking change to `master`
cuts the release itself** (bump → tag → npm publish → GitHub Release); chore/docs-only
merges skip cleanly. A manual **Actions → Bump version** dispatch is still available. The
full runbook (and the one-time `NPM_TOKEN` setup) is in
[docs/RELEASING.md](./docs/RELEASING.md).

## Sign your work (DCO)

Expand Down
27 changes: 14 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,14 +220,14 @@ Structural differences only — each row is checkable against the named source,
tables (including what each adjacent tool does _better_) are in
[`reports/benchmarks.md` → Uniqueness](reports/benchmarks.md#uniqueness--structural-contrasts-with-adjacent-tools):

| Property | Forge | Note stores / gateways / RAG |
| -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Memory confidence moved **only by independent oracles** (tests, CI, human) | yes — closed `ORACLES` table; unverifiable evidence rejected (`src/ledger.js`) | note stores keep notes as written |
| Unreviewed knowledge decays toward _uncertainty_, not deletion | yes — time-decayed Beta posterior; dormant claims kept for audit | notes persist unchanged until deleted |
| Conflict-free team merge over plain git | yes — join-semilattice, property-tested | per-machine SQLite or a hosted store |
| Routing decision visible and diffable **before** dispatch | yes — deterministic rubric over `src/model_tiers.json` | gateways decide inside the proxy at request time |
| Cached code served **only with verification evidence**, revalidated against the current code graph | yes — `SERVE_FLOOR`, `revalidate()` in `src/reuse.js` | plain RAG serves on similarity alone |
| **What they do better** | — | hosted sync, web UIs, embedding search that catches paraphrase; gateways actually _move traffic_ (failover, quotas). Forge is a transparency layer, not a replacement |
| Property | Forge | Note stores / gateways / RAG |
| -------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Memory confidence moved **only by independent oracles** (tests, CI, human) | yes — closed `ORACLES` table; unverifiable evidence rejected (`src/ledger.js`) | note stores keep notes as written |
| Unreviewed knowledge decays toward _uncertainty_, not deletion | yes — confidence fades over time toward _unsure_; dormant claims kept for audit, never deleted | notes persist unchanged until deleted |
| Conflict-free team merge over plain git | yes — two teammates' memories combine by set-union, so they never conflict (property-tested) | per-machine SQLite or a hosted store |
| Routing decision visible and diffable **before** dispatch | yes — a deterministic rubric you can read in the repo (`src/model_tiers.json`) | gateways decide inside the proxy at request time |
| Cached code served **only with verification evidence**, revalidated against the current code graph | yes — a cache hit is served only if its evidence clears a confidence floor and still matches today's code | plain RAG serves on similarity alone |
| **What they do better** | — | hosted sync, web UIs, embedding search that catches paraphrase; gateways actually _move traffic_ (failover, quotas). Forge is a transparency layer, not a replacement |

## Honest limits

Expand All @@ -245,11 +245,12 @@ _advisory_. **Tests and human corrections always win.** Full list:

## Why a cognitive substrate? The white paper

A language model at inference is a fixed function `y = f(x)` — frozen weights, a bounded
window, no state between calls. Memory, foresight, and self-checking can't be prompted into
that shape; they have to be supplied from outside. The full argument, with every
load-bearing statistic re-graded against primary sources, is the
[cognitive-substrate white paper](docs/cognitive-substrate/).
A model can't learn from your codebase between calls: its weights are frozen and its
working memory is wiped after every response. Memory, foresight, and self-checking can't
be prompted into it — they have to be supplied from outside, which is what the substrate
does. (Formally: inference is a fixed function `y = f(x)` with no state between calls.)
The full argument, with every load-bearing statistic re-graded against primary sources, is
the [cognitive-substrate white paper](docs/cognitive-substrate/).

## Public site

Expand Down
Loading
Loading