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
1 change: 1 addition & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
# Powers the "Sponsor" button on the repo. Add funding platforms here (github, ko_fi, …).
github: [CodeWithJuber]
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# CI gate for every push and PR: test matrix (Node 18/20/22), Biome lint+format, ShellCheck,
# the zero-runtime-dependency assertion, and dependency review. All must pass to merge.
name: CI

on:
Expand Down
9 changes: 5 additions & 4 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ packages; the production runtime remains Node-only and zero-dependency.
## Architecture — a four-layer config compiler with ONE source

```
source/AGENTS.md (canonical rules: git · testing · security · style)
source/rules.json (canonical rules: git · testing · security · style)
(+ source/substrate.json, source/mcp.json)
forge sync (emitter: content-hash + DO-NOT-EDIT headers)
┌───────────────┬───────┴───────┬───────────────┬─────────────┐
Claude CLAUDE.md Codex AGENTS.md Cursor .mdc Gemini settings Aider .aider.conf.yml ...
Expand Down Expand Up @@ -133,8 +133,9 @@ forgekit/
doctor.js # health checks
emit/ # one module per tool (claude, codex, cursor, gemini, aider, copilot, windsurf, zed, continue) + mcp
source/
AGENTS.md # THE canonical source
rules/ # git.md, testing.md, security.md, style.md → assembled
rules.json # THE canonical rules source (git · testing · security · style)
substrate.json # cognitive-substrate defaults (thresholds, routing, llm knobs)
mcp.json # MCP server definitions emitted into each tool
global/ # installs into ~/.forge, symlinked into ~/.claude
tools/ crew/ guards/ mcp/atlas/ recall/ lean/ statusline.sh settings.template.json
templates/project-layer/ # per-repo template (was hostlelo-project-layer)
Expand Down
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

### Added

- **Opt-in LLM adjudication for the substrate (`FORGE_LLM=1`)** — one shared, fail-safe `claude -p` proposer (`src/adjudicate.js`) wired thinly into the assumption gate (M2), model routing (M1), impact/blast-radius, and goal-drift (M4). The model only *proposes*; every proposal is verified against the deterministic rubric, the code graph, or a grep before it can move a verdict (routing raise-only; the gate tightens-only; impact edges must resolve and grep-confirm; goal-drift off→on only). Off by default — behaviour is unchanged unless enabled — never blocks, and the ambient Claude Code hook stays deterministic unless `FORGE_LLM_AMBIENT=1`. `forge substrate --json` now carries an `llm.provenance` map (`deterministic` / `llm-verified` / `llm-agreed`) per faculty for auditability.
- **Opt-in LLM adjudication for the substrate (`FORGE_LLM=1`)** — one shared, fail-safe `claude -p` proposer (`src/adjudicate.js`) wired thinly into the assumption gate (M2), model routing (M1), impact/blast-radius, and goal-drift (M4). The model only *proposes*; every proposal is verified against the deterministic rubric, the code graph, or a grep before it can move a verdict. Off by default — behaviour is unchanged unless enabled — never blocks, and the ambient Claude Code hook stays deterministic unless `FORGE_LLM_AMBIENT=1`. `forge substrate --json` carries an `llm.provenance` map per faculty for auditability.
- **Bidirectional verified reconcile (default on when `FORGE_LLM=1`; `llm.bidirectional` in `source/substrate.json` to disable).** A verified reading may now *reduce* caution as well as add it — clear a false "ASK FIRST" (`llm-cleared`) and route a task *down* a tier (`llm-lowered`) — but only within `band` and never past the hard floors: the gate can't clear a task with no concrete anchor or one naming symbols/files the repo lacks, and routing can't drop below a strong-signal (algorithmic/architectural) floor. Set `llm.bidirectional: false` for the conservative tighten-/raise-only mode. Impact edges stay graph-+-grep-verified; goal-drift stays off→on with a goal-referencing reason.
- **Explicit memory `val` term** — lesson retrieval now decomposes into the white paper's `relevance × freshness × validity × scope`, with `validity()` (a ground-truth Beta posterior over confirmed vs. contradicted outcomes) exported and ranked so outcome-confirmed lessons outrank merely-recent ones.

### Changed
Expand Down
14 changes: 9 additions & 5 deletions docs/GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -429,11 +429,15 @@ Create `global/crew/<name>.md` with frontmatter. It installs into `~/.claude/age
By default every judgment is a deterministic rubric. `FORGE_LLM=1` adds a thin **proposer**
layer (`src/adjudicate.js`) — one shared `claude -p` primitive used by M2/M1/impact/M4. The
model never decides: each proposal is verified against the rubric, the code graph, or a grep
before it can move a verdict (routing raise-only; the gate tightens-only; impact edges must be
real + grep-confirmed; goal-drift off→on only). Any failure falls back to the deterministic
path, so the flag is safe to leave off or on. `--json` exposes `llm.provenance` per faculty.
Each faculty pairs a pure `*LLM` proposer with a `reconcile` step — extend by adding both,
never by trusting the model's answer directly.
before it can move a verdict. The reconcile is **bidirectional but rail-guarded** by default —
a verified reading can *clear* a false ask or route a task *down* a tier, not only add caution,
but never past a hard floor (no concrete anchor, unresolved repo entities, or a strong-signal
routing floor). Impact edges must be real + grep-confirmed; goal-drift moves off→on only. Any
failure falls back to the deterministic path, so the flag is safe to leave off or on. `--json`
exposes `llm.provenance` per faculty (`llm-cleared` / `llm-tightened` / `llm-raised` /
`llm-lowered` / …). Set `llm.bidirectional: false` in `source/substrate.json` for the
conservative tighten-/raise-only mode. Each faculty pairs a pure `*LLM` proposer with a
`reconcile` step — extend by adding both, never by trusting the model's answer directly.

### Support a new tool
Add an emitter module in `src/emit/<tool>.js` (mirror an existing one like
Expand Down
42 changes: 33 additions & 9 deletions docs/cognitive-substrate/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,19 +185,43 @@ 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).

The model is **never the judge — only a proposer.** Every proposal is *verified* before it can
move a verdict, in the direction of the paper's *tabayyun* gate (49:6):
```mermaid
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\nllm-cleared / lowered / raised / verified]
C -->|fails / unavailable| D[verdict holds\ndeterministic]
```

- **routing** can only be *raised* (`max` with the rubric), never lowered on the model's word;
- **the assumption gate only tightens** — it can add an ask, never clear a deterministic one;
- **impact edges** are kept only if the file is real *and* a grep confirms the reference;
- **goal-drift** rescues an off-goal file only with a goal-referencing reason (off→on only).
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.

The model is **never the judge — only a proposer.** Every proposal is *verified* before it can
move a verdict, in the direction of the paper's *tabayyun* gate (49:6). By default the reconcile
is **bidirectional but rail-guarded** — a verified reading can lower caution as well as raise it,
but never past a hard floor:

- **routing** — a *raise* is free (spotting hidden complexity costs at most a bigger model); a
*lower* is bounded to one band and never drops below a strong-signal (algorithmic/architectural)
floor, so a "distributed rate-limiter" can't be talked down to the cheap tier;
- **the assumption gate** — can *clear* a false ask **or** *add* one, but never clears a task
with no concrete anchor, or one naming symbols/files the repo doesn't define;
- **impact edges** — kept only if the file is real *and* a grep confirms the reference;
- **goal-drift** — rescues an off-goal file only with a goal-referencing reason (off→on only).

> **Note** — set `llm.bidirectional: false` in
> [`source/substrate.json`](../../source/substrate.json) for the conservative tighten-/raise-only
> mode (caution can only ever increase).

It is **fail-safe**: any error, timeout, or unparseable reply falls back to the deterministic
path (behaviour is byte-identical with the flag off), and it **never blocks**. `--json` output
carries a `llm.provenance` map (`deterministic` / `llm-verified` / `llm-agreed`) per faculty so
every model-touched decision is auditable. Off by default; the ambient Claude Code hook stays
deterministic unless you also set `FORGE_LLM_AMBIENT=1`. Config lives in
carries an `llm.provenance` map (`deterministic` / `llm-cleared` / `llm-tightened` /
`llm-raised` / `llm-lowered` / `llm-verified`) per faculty so every model-touched decision is
auditable. Off by default; the ambient Claude Code hook stays deterministic unless you also set
`FORGE_LLM_AMBIENT=1`. Config lives in
[`source/substrate.json`](../../source/substrate.json) → `llm`.

---
Expand Down
5 changes: 5 additions & 0 deletions docs/legacy/PLAYBOOK.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Playbook — how to drive your Claude setup

> ⚠️ **Archived — pre-Forge.** Kept for history; **not maintained**. It describes an older
> hand-rolled `~/.claude` setup, not today's `forge` CLI. For the current workflow start at
> **[ONBOARDING.md](../../ONBOARDING.md)** → **[docs/GUIDE.md](../GUIDE.md)**. Any credentials or
> open action items below are historical and superseded — do not act on them.

Your global config auto-loads skills/rules by what you ask; you rarely invoke them
by name. Below: the exact prompt per situation, and what fires under the hood.

Expand Down
5 changes: 5 additions & 0 deletions docs/legacy/RECONCILE.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Audit of your live ~/.claude + how this bundle fits

> ⚠️ **Archived — pre-Forge.** Kept for history; **not maintained**. It describes an older
> hand-rolled `~/.claude` setup, not today's `forge` CLI. For the current workflow start at
> **[ONBOARDING.md](../../ONBOARDING.md)** → **[docs/GUIDE.md](../GUIDE.md)**. Any credentials or
> open action items below are historical and superseded — do not act on them.

Read from your actual machine on 2026-07-04. Honest feedback, then what changed.

## 🔴 Fix now
Expand Down
4 changes: 4 additions & 0 deletions docs/legacy/RESEARCH-claude-config.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Deep research — best Claude Code configs vs. yours (2026)

> ⚠️ **Archived — pre-Forge.** Kept for history; **not maintained**. Research notes from the
> pre-Forge era. For the current design see **[docs/cognitive-substrate/README.md](../cognitive-substrate/README.md)**
> and **[ARCHITECTURE.md](../../ARCHITECTURE.md)**.

Multi-source, cross-checked. Verdict up front: **your config already matches or
exceeds documented community best practice on ~90% of dimensions.** Two genuine
gaps remain. Unbiased finding: the quality move now is *consolidation, not adding
Expand Down
5 changes: 5 additions & 0 deletions docs/legacy/RUN.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# RUN — command sheet (current state)

> ⚠️ **Archived — pre-Forge.** Kept for history; **not maintained**. It describes an older
> hand-rolled `~/.claude` setup, not today's `forge` CLI. For the current workflow start at
> **[ONBOARDING.md](../../ONBOARDING.md)** → **[docs/GUIDE.md](../GUIDE.md)**. Any credentials or
> open action items below are historical and superseded — do not act on them.

Bundle: `~/Downloads/claude-global-config/`. Most of this is **already applied
live** on your machine. This is the accurate final command sheet.

Expand Down
3 changes: 3 additions & 0 deletions docs/legacy/reports/2026-07-04.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Daily Config Radar — 2026-07-04

> ⚠️ **Archived — pre-Forge.** A historical auto-generated report, kept for the record. Not
> maintained and not the current state. See **[docs/GUIDE.md](../../GUIDE.md)** for today's tooling.

**TL;DR**
- 🔺 **Claude Code 2.1.200** (Jul 3) changes the *default permission mode to "Manual"* and stops AskUserQuestion auto-continuing — your `settings.json` uses `defaultMode: "default"`, worth reviewing. Subagents now run in the background by default (helps your `scout`/`verifier` flow).
- 🎨 **shadcn/ui** made **Base UI the default** (over Radix) as of July 2026 — your `ui-workflow` skill + shadcn MCP should expect Base UI in new projects.
Expand Down
6 changes: 5 additions & 1 deletion source/substrate.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@
"enabled": false,
"model": "haiku",
"timeoutMs": 20000,
"note": "Opt-in (FORGE_LLM=1). The model PROPOSES; the deterministic rubric + repo grounding + tests VERIFY. Any failure falls back to the deterministic path — never blind, never blocking."
"bidirectional": true,
"band": 0.25,
"routingBand": 0.2,
"signalFloor": 0.4,
"note": "Opt-in (FORGE_LLM=1). The model PROPOSES; the deterministic rubric + repo grounding + tests VERIFY. Any failure falls back to the deterministic path — never blind, never blocking. `bidirectional` (default true) lets a verified reading also clear a false ask / route a tier down, within `band`/`routingBand` and never past the hard floors (`signalFloor`, repo grounding, no-anchor). Set false for the conservative tighten/raise-only mode."
},
"policies": {
"preAction": "Run substrate before ambiguous, expensive, multi-file, or mutating work.",
Expand Down
62 changes: 52 additions & 10 deletions src/preflight.js
Original file line number Diff line number Diff line change
Expand Up @@ -248,20 +248,33 @@ export function assessTaskLLM(task, { run = buildRunner() } = {}) {

/**
* Verify-don't-trust reconcile for M2. The model may only move completeness within ±band of the
* deterministic score (so a clearly-specified/vague task can't be flipped), and may only ADD an
* ask, never clear a deterministic one. Extra questions survive only if they map to a rubric-
* flagged dimension or (via `grounded`) reference a real repo entity.
* deterministic score, so a clearly-specified or clearly-vague task can never be flipped — only a
* borderline reading shifts. In `bidirectional` mode (default) the ask is recomputed purely from
* that bounded completeness, so a verified reading can also CLEAR a false ask — but two hard
* floors the model can never override still force the ask: a task with no concrete anchor
* (`hardUnderspecified`), or one naming symbols/files the repo doesn't define (`hasUnresolved`).
* With `bidirectional:false` the gate only ever tightens (the conservative pre-bidirectional
* behaviour). Extra questions survive only if they map to a rubric-flagged dimension or (via
* `grounded`) reference a real repo entity.
* @param {object} det - assessTask() result
* @param {{completeness:number, missing:string[], questions:string[]}|null} proposal
* @param {object} [opts]
* @param {number} [opts.askThreshold]
* @param {number} [opts.band]
* @param {(q:string)=>boolean} [opts.grounded]
* @param {boolean} [opts.bidirectional]
* @param {boolean} [opts.hasUnresolved]
*/
export function reconcileAssumption(
det,
proposal,
{ askThreshold = 0.6, band = 0.25, grounded = () => false } = {},
{
askThreshold = 0.6,
band = 0.25,
grounded = () => false,
bidirectional = true,
hasUnresolved = false,
} = {},
) {
if (!proposal) return { ...det, provenance: { path: "deterministic" } };
const bounded = Math.max(
Expand All @@ -274,12 +287,18 @@ export function reconcileAssumption(
(q) => proposal.missing.some((m) => flaggedDims.has(m)) || grounded(q),
);
const questions = [...new Set([...det.questions, ...extraQuestions])].slice(0, 3);
// The gate only ever tightens: an ask stands if the rubric asked, the task is hard-
// underspecified, or the reconciled completeness is below threshold.
const shouldAsk = det.shouldAsk || det.hardUnderspecified || completeness < askThreshold;
// Bidirectional (default): the ask follows the bounded completeness, guarded by two floors the
// model can't override. Tighten-only: the rubric's ask always stands, the model can only add one.
const shouldAsk = bidirectional
? det.hardUnderspecified || hasUnresolved || completeness < askThreshold
: det.shouldAsk || det.hardUnderspecified || completeness < askThreshold;
const risk = completeness < 0.45 ? "high" : completeness < 0.7 ? "medium" : "low";
const moved =
Math.abs(completeness - det.completeness) > 1e-9 || questions.length !== det.questions.length;
let path;
if (shouldAsk && !det.shouldAsk) path = "llm-tightened";
else if (!shouldAsk && det.shouldAsk) path = "llm-cleared";
else path = moved ? "llm-verified" : "llm-agreed";
return {
...det,
completeness,
Expand All @@ -289,7 +308,7 @@ export function reconcileAssumption(
shouldAsk && !questions.length
? ["What exactly should this produce, and how will we know it is correct?"]
: questions,
provenance: { path: moved ? "llm-verified" : "llm-agreed", detCompleteness: det.completeness },
provenance: { path, detCompleteness: det.completeness },
};
}

Expand Down Expand Up @@ -342,11 +361,22 @@ export function clarifyBlock(result, { threshold = 0.5 } = {}) {
* @param {string} [opts.model]
* @param {number} [opts.timeoutMs]
* @param {(p:string)=>string} [opts.run]
* @param {boolean} [opts.bidirectional]
* @param {number} [opts.band]
*/
export function preflightRepo(
root,
text,
{ allowBuild = true, askThreshold = 0.6, llm, model, timeoutMs, run } = {},
{
allowBuild = true,
askThreshold = 0.6,
llm,
model,
timeoutMs,
run,
bidirectional = true,
band,
} = {},
) {
const atlas = loadAtlas(root) || (allowBuild ? buildAtlas({ root }) : null);
const hasSymbol = atlas ? (name) => has(atlas, name) : () => true;
Expand All @@ -364,5 +394,17 @@ export function preflightRepo(
const { symbols, files } = referencedEntities(q);
return symbols.some(hasSymbol) || files.some((f) => existsSync(join(root, f)));
};
return { ...gap, assumption: reconcileAssumption(det, proposal, { askThreshold, grounded }) };
// Repo grounding is a hard floor on clearing: if the task names entities the repo lacks, the
// model can never wave the gate through no matter how "complete" it judges the prose.
const hasUnresolved = gap.unresolved.symbols.length + gap.unresolved.files.length > 0;
return {
...gap,
assumption: reconcileAssumption(det, proposal, {
askThreshold,
grounded,
bidirectional,
hasUnresolved,
...(typeof band === "number" ? { band } : {}),
}),
};
}
Loading
Loading