Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
0379602
Document how to generate viable simple-cause statements.
AdamSpitz Aug 27, 2026
44c5cdf
Copy exercise-1 simple causes into live seed; geo rollup still open.
AdamSpitz Aug 27, 2026
fa8d90f
Added a code base cleanup plan.
AdamSpitz Aug 27, 2026
4180d60
Added thoughts on belief implication, geographic containment, etc.
AdamSpitz Aug 27, 2026
06298dc
Clean up dead code surfaces
AdamSpitz Aug 27, 2026
2db9f78
Clean up ABI sync and indexer registration
AdamSpitz Aug 27, 2026
53972fb
Add geographic project inclusion rules
AdamSpitz Aug 27, 2026
3f90e24
Clean up duplicated constants and helpers
AdamSpitz Aug 27, 2026
eb3f653
Treat nested-place rollup as board inclusion, not implication.
AdamSpitz Aug 27, 2026
93f3128
Draft exercise-2 abortion triple; thicken modified-right after attester.
AdamSpitz Aug 27, 2026
f23016a
Switch OpenRouter services to DeepSeek V4 Flash.
AdamSpitz Aug 27, 2026
a2c34ee
Fold CauseStarter onto the shared UI runtime.
AdamSpitz Aug 27, 2026
2f03e38
Narrow SDK public utils and drop leftover GraphQL compile glue.
AdamSpitz Aug 27, 2026
339083d
Reject nested-place geography as belief implication.
AdamSpitz Aug 27, 2026
b2f6caa
Split SDK eventDecoder by subsystem with explicit ABIs.
AdamSpitz Aug 27, 2026
ca0dcb6
Split SDK query modules and read chain state via generated ABIs.
AdamSpitz Aug 27, 2026
442ad14
Extract bridge-cluster publish; drop the cleanup plan.
AdamSpitz Aug 27, 2026
275f15e
Keep unscoped rows visible on geographic boards.
AdamSpitz Aug 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
4 changes: 3 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@
# External services (set in .env.secrets)
# =============================================================================
# OPENROUTER_API_KEY=sk-...
# XAI_API_KEY=xai-... # cause-assist (Grok)
# OPENROUTER_MODEL=deepseek/deepseek-v4-flash-0731 # production services only
# DEV_OPENROUTER_MODEL=deepseek/deepseek-v4-flash-0731 # laptop scripts (fake-data-generation); does not affect services
# XAI_API_KEY=xai-... # unused if OPENROUTER_API_KEY is set (cause-assist / worker fallback)
# VITE_WALLETCONNECT_PROJECT_ID=...
# PINATA_JWT=...
BASE_SEPOLIA_RPC_URL=https://sepolia.base.org
Expand Down
5 changes: 1 addition & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
.DS_Store
.envrc
node_modules
**/dist/
.env
.env.secrets
.env.render
Expand All @@ -9,8 +10,6 @@ services/implication-attester/.env
ui/.env
integration-tests/.env.local
typechain-types/
services/implication-attester/dist
published-data-ipfs-mirror/dist/
hardhat/deployments/
deployments/localhost.env
deployments/localhost.contracts-manifest.json
Expand All @@ -35,10 +34,8 @@ tmp/


# Local build outputs for new packages
causestarter/dist/
# Generated at container startup; local copies may contain environment-specific addresses.
/causestarter/public/config.json
cause-assist/dist/

# pi subagent scratch artifacts
.pi-subagents/
9 changes: 9 additions & 0 deletions CONTINUITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -1790,3 +1790,12 @@ end-user docs; **cause board** is the CauseStarter organizer publication
## 2026-08-20 — Anvil `--state` restart dump

Local `hardhat-node` was coming back empty after `stack.restart-consistency` because Docker SIGTERM did not make Anvil dump `/data/state.json` (then a fresh deploy + trust wiring filled ~41 blocks with no seed). Fix: `scripts/anvil-docker-entrypoint.sh` maps SIGTERM→SIGINT, compose `--state-interval 15` and 60s grace, restart check records block height and SIGINTs Anvil before stop. Recreate `hardhat-node` to mount the wrapper.

## 2026-08-27 — Statement generation process + exercise 1

Process: [`fake-data-generation/statement-generation.md`](fake-data-generation/statement-generation.md). Fresh-instance handoff: [`continuity/2026-08-27-statement-generation.md`](continuity/2026-08-27-statement-generation.md).

Exercise 1 (simple causes, no triples) gold set remains in `fake-data-generation/statement-generation-exercises/01-simple-causes.json`. Live copy: `fake-data-generation/seed-content/simple-causes.json`. Nested-place rollup is board inclusion, not implication (Ontario-wide planks are genuine wants; garden relevant areas + roster `within` Ontario). `loadSeedCollections` still does not read the exercises directory. Tiny seed still uses the explorer slogan for the garden project. Nested-place pairs are designed-no: `npm run gen:seed:simple-causes-implications`. Cause-assist `STATEMENT_QUALITY_GUIDANCE` includes: want the outcome (not “X is a public good”), do not plank payroll, earmark grain as a ladder (kind + place), do not emit geo implication parents. Rebuild Docker cause-assist to serve the new prompt.

Do not train implication generation on Christianity × secular-conservatism. Next: curriculum exercise 2. The implication attester prompt now rejects nested-place geographic rollup (Grey County → Ontario is a worked reject, not an accept).

15 changes: 14 additions & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,20 @@ When an item from this page is done and no longer needs an LLM implementor's att

----

- **(Tell)** Refresh `data/seed-implication-evaluations.original-variants.json`
against the current implication-attester prompt fingerprint. The prompt now
rejects nested-place geographic rollup (Grey County → Ontario is a worked
false); the checked-in corpus still has the old fingerprint, so
`test:seed:implication-regression` will fail until a dedicated pass re-evaluates
the 1870 original↔variant pairs. Do not restamp fingerprints without live
decisions, and do not paper over it in seed wording. A v4-flash pass stalled
on empty LLM completions — use a model that actually returns JSON. Resume
already skips only pairs with the current fingerprint. Personalized AI ranking
remains deferred per
[belief-implication-board-inclusion-and-discovery.md](specs/product/belief-implication-board-inclusion-and-discovery.md).

- **(Ask)** Statement-generation exercise 2: abortion cutoff triple is in [`fake-data-generation/statement-generation-exercises/02-compromise-abortion.json`](fake-data-generation/statement-generation-exercises/02-compromise-abortion.json); modified-right was thickened after the attester refused the old text. Next: confirm attester blesses both modifieds, run `/critique-triple`, then Adam accept/reject before `seed-content/`.

- **(Tell)** After folding CauseStarter into `ui/src/causestarter/`, leftover package
glue still talks as if `causestarter/src` is the SPA: `causestarter/vite.config.ts`
is unused, Compose/Docker docs mix `:8090` and `:5174`, and some verifier prompts
Expand Down Expand Up @@ -58,4 +72,3 @@ When an item from this page is done and no longer needs an LLM implementor's att
the user has moved to another statement can paint stale content. Pre-existing, not
new to the combinator work; the same pattern is worth a sweep across the other
conceptspace pages. Found 2026-08-19.

10 changes: 5 additions & 5 deletions cause-assist/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,12 @@ Without an API key, the suggester uses conservative local templates, implication

| Env | Default | Notes |
| --- | --- | --- |
| `XAI_API_KEY` | — | xAI key — set in repo-root `.env.secrets`, then `./scripts/setup-env.sh` |
| `OPENROUTER_API_KEY` | — | Legacy fallback if no xAI/Grok key; pairs with OpenRouter base URL + `x-ai/grok-4.5` model defaults |
| `CAUSE_ASSIST_API_BASE_URL` | `https://api.x.ai/v1` (or OpenRouter when only `OPENROUTER_API_KEY` is set) | OpenAI-compatible base URL |
| `CAUSE_ASSIST_SUGGEST_MODEL` | `grok-4.5` (or `x-ai/grok-4.5` for OpenRouter-only) | Suggester model id |
| `OPENROUTER_API_KEY` | — | Preferred LLM key; pairs with OpenRouter + `deepseek/deepseek-v4-flash-0731` |
| `XAI_API_KEY` | — | Fallback only when OpenRouter is not configured |
| `CAUSE_ASSIST_API_BASE_URL` | `https://openrouter.ai/api/v1` when OpenRouter is used | OpenAI-compatible base URL |
| `CAUSE_ASSIST_SUGGEST_MODEL` | production OpenRouter model (`deepseek/deepseek-v4-flash-0731`) | Suggester model id |
| `CAUSE_ASSIST_COHERENCE_MODEL` | same as safety/suggest | Roster coherence model (own slot so it is not generation's model by accident) |
| `CAUSE_ASSIST_SAFETY_MODEL` | `grok-4.5` (or `x-ai/grok-4.5` for OpenRouter-only) | Safety filter model id |
| `CAUSE_ASSIST_SAFETY_MODEL` | production OpenRouter model | Safety filter model id |
| `CAUSE_ASSIST_IMPLICATION_MODEL` | same as suggest model | Implication check model id |
| `CAUSE_ASSIST_COHERENCE_ATTESTER_ADDRESS` | — | Public worker address exposed by `/health`; the HTTP process does not receive its private key |
| `PORT` / `CAUSE_ASSIST_PORT` | `3002` | HTTP port |
Expand Down
2 changes: 2 additions & 0 deletions cause-assist/eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import js from '@eslint/js'
import codeMetrics from '../eslint.metrics.mjs'
import tseslint from 'typescript-eslint'
import { defineConfig, globalIgnores } from 'eslint/config'

export default defineConfig([
...codeMetrics,
globalIgnores(['dist']),
{
files: ['**/*.ts'],
Expand Down
2 changes: 2 additions & 0 deletions cause-assist/src/bridgeClusterAssist.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ const config: CauseAssistConfig = {
describe('statement guidance routing', () => {
it('keeps signer-annoyance routing on bridge drafts, not ordinary cause verbs', () => {
assert.doesNotMatch(STATEMENT_QUALITY_GUIDANCE, /annoyed at being asked/)
assert.doesNotMatch(STATEMENT_QUALITY_GUIDANCE, /must imply "I want more CSA in Ontario"/)
assert.match(STATEMENT_QUALITY_GUIDANCE, /board inclusion rule/)
assert.match(BRIDGE_STATEMENT_GUIDANCE, /annoyed at being asked to also sign the shared plank/)
})
})
Expand Down
24 changes: 18 additions & 6 deletions cause-assist/src/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { describe, it } from 'mocha'
import { loadConfigFromEnv } from './config.js'

describe('loadConfigFromEnv', () => {
it('defaults to xAI base URL and models when XAI key is set', () => {
it('defaults to xAI base URL and models when only an XAI key is set', () => {
const config = loadConfigFromEnv({
XAI_API_KEY: 'xai-test-key',
})
Expand All @@ -14,24 +14,36 @@ describe('loadConfigFromEnv', () => {
assert.equal(config.implicationModel, 'grok-4.5')
})

it('pairs OPENROUTER_API_KEY with OpenRouter base URL and model ids', () => {
it('pairs OPENROUTER_API_KEY with OpenRouter base URL and production model ids', () => {
const config = loadConfigFromEnv({
OPENROUTER_API_KEY: 'or-test-key',
})
assert.equal(config.apiKey, 'or-test-key')
assert.equal(config.apiBaseUrl, 'https://openrouter.ai/api/v1')
assert.equal(config.suggestModel, 'x-ai/grok-4.5')
assert.equal(config.safetyModel, 'x-ai/grok-4.5')
assert.equal(config.implicationModel, 'x-ai/grok-4.5')
assert.equal(config.suggestModel, 'deepseek/deepseek-v4-flash-0731')
assert.equal(config.safetyModel, 'deepseek/deepseek-v4-flash-0731')
assert.equal(config.implicationModel, 'deepseek/deepseek-v4-flash-0731')
})

it('prefers xAI key over OpenRouter and keeps xAI defaults', () => {
it('prefers OpenRouter when both keys are set', () => {
const config = loadConfigFromEnv({
XAI_API_KEY: 'xai-test-key',
OPENROUTER_API_KEY: 'or-test-key',
})
assert.equal(config.apiKey, 'or-test-key')
assert.equal(config.apiBaseUrl, 'https://openrouter.ai/api/v1')
assert.equal(config.suggestModel, 'deepseek/deepseek-v4-flash-0731')
})

it('uses the xAI key when an explicit xAI base is set even if both keys exist', () => {
const config = loadConfigFromEnv({
XAI_API_KEY: 'xai-test-key',
OPENROUTER_API_KEY: 'or-test-key',
CAUSE_ASSIST_API_BASE_URL: 'https://api.x.ai/v1',
})
assert.equal(config.apiKey, 'xai-test-key')
assert.equal(config.apiBaseUrl, 'https://api.x.ai/v1')
assert.equal(config.suggestModel, 'grok-4.5')
})

it('honors explicit base URL even with only OpenRouter key', () => {
Expand Down
15 changes: 7 additions & 8 deletions cause-assist/src/config.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { PRODUCTION_OPENROUTER_MODEL } from '@commonality/attester-core'
import type { CauseAssistConfig } from './types.js'

const DEFAULT_XAI_BASE_URL = 'https://api.x.ai/v1'
const DEFAULT_OPENROUTER_BASE_URL = 'https://openrouter.ai/api/v1'
const DEFAULT_MODEL = 'grok-4.5'
const DEFAULT_OPENROUTER_MODEL = 'x-ai/grok-4.5'
const DEFAULT_XAI_MODEL = 'grok-4.5'

function firstEnv(env: NodeJS.ProcessEnv, keys: string[]): string | undefined {
for (const key of keys) {
Expand All @@ -15,17 +15,16 @@ function firstEnv(env: NodeJS.ProcessEnv, keys: string[]): string | undefined {

export function loadConfigFromEnv(env: NodeJS.ProcessEnv = process.env): CauseAssistConfig {
const xaiKey = firstEnv(env, ['XAI_API_KEY'])
// Legacy fallback if someone still only has OpenRouter configured.
const openRouterKey = firstEnv(env, ['OPENROUTER_API_KEY'])
const apiKey = xaiKey || openRouterKey
const usingOpenRouterOnly = !xaiKey && Boolean(openRouterKey)

const explicitBase = firstEnv(env, ['CAUSE_ASSIST_API_BASE_URL', 'XAI_API_BASE_URL'])
const baseLooksOpenRouter = (explicitBase ?? '').includes('openrouter.ai')
const usingOpenRouter = explicitBase ? baseLooksOpenRouter : Boolean(openRouterKey)
const apiKey = usingOpenRouter ? (openRouterKey || xaiKey) : (xaiKey || openRouterKey)
const apiBaseUrl =
explicitBase ||
(usingOpenRouterOnly ? DEFAULT_OPENROUTER_BASE_URL : DEFAULT_XAI_BASE_URL)
(usingOpenRouter ? DEFAULT_OPENROUTER_BASE_URL : DEFAULT_XAI_BASE_URL)

const defaultModel = usingOpenRouterOnly ? DEFAULT_OPENROUTER_MODEL : DEFAULT_MODEL
const defaultModel = usingOpenRouter ? PRODUCTION_OPENROUTER_MODEL : DEFAULT_XAI_MODEL

return {
apiKey,
Expand Down
4 changes: 4 additions & 0 deletions cause-assist/src/plankStrategies.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ describe('plank-first strategies', () => {
it('atomizes a rough bundle without imposing main-to-supporting implications', async () => {
const result = await atomizeCause({ description: 'local resilience', count: 2 }, config, async <T>(request: LlmJsonRequest) => {
assert.match(request.systemPrompt, /coalition unbundling/i)
assert.match(request.systemPrompt, /Want more of the thing, do not classify it/)
assert.match(request.systemPrompt, /Do not write "I want people who do X to get paid"/)
assert.match(request.systemPrompt, /Prefer earmark grain/)
assert.match(request.systemPrompt, /board inclusion rule/)
assert.doesNotMatch(request.userPrompt, /mainStatement/)
return { planks: [
{ text: 'Our neighborhood should maintain a shared emergency food pantry.', rationale: 'food resilience' },
Expand Down
4 changes: 4 additions & 0 deletions cause-assist/src/statementGuidance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ export const STATEMENT_QUALITY_GUIDANCE = `What a statement is (Commonality / Ca
- Aim for determinate meaning, not exhaustive detail. A broad proposition may leave implementation open and still be clear. Reject wording only when sincere readers could assign materially different propositions to it.
- Statements must be self-contained. Do not use slogans, tribe-markers, or shorthand that needs unstated background context (e.g. reject "I am pro-choice" as not clear enough by itself).
- Prefer concrete, signable claims over marketing fluff, mission slogans, or vague aspirations.
- Want more of the thing, do not classify it. Write "I want more neighborhood gardens" / "I want widely used library L to stay maintained and well-documented" — not "X is a public good", "X is a worthwhile local public good", or "material support is a legitimate way to keep X available." A signer is saying they want the outcome; a project attests it is aligned with that want. Taxonomy ("this is a public good") is our language, not theirs.
- Do not write "I want people who do X to get paid" / unpaid nights-and-weekends / "material support for maintainers." Paying the work is what Commonality is for. A funding project aligns with the desired work-product ("I want this library maintained and documented"), not with a meta-claim that labor should be compensated.
- Prefer earmark grain, not only a category. Grain is a ladder on more than one axis, and the useful axis depends on the cause. Software: kind of software (OSS → Linux → Linux desktop; Ethereum → Ethereum-based gaming; a named library). Food: kind of system (gardens, CSA, farmers' markets) *and* place ("I want more CSA in Grey County, Ontario"). Place is often the fire-and-forget earmark for local public goods. A general want is still fine for showing general support, advocacy, or delegating a monthly amount to someone who then picks projects. When atomizing a broad cause, propose the general plank *and* several more-specific wants at more than one grain and, where the cause is local, at least one place-specialized want. Do not stop at the category, and do not treat "Linux" / "CSA" as the most specific you may go.
- Place-specialized wants are ordinary signable planks. "I want more CSA in Grey County, Ontario" does not imply "I want more CSA in Ontario" and should not be minted so that implication can fill a province board. Nested-place project discovery is a board inclusion rule (project relevant-area paths plus optional board "within"), not belief implication. A province-wide want is a different statement, for people who actually hold that goal. Do not emit weaker geo "parents" for rollup, drop "more" to buy a bless, use "somewhere in REGION" as a parent dialect, or mint an any-combinator over known counties.
- Statements are public and permanent. Do not invent illegal, fraudulent, hateful, doxxing, sanctions-evading, or election-campaign-fundraising content. No personal contact details or private identifiers.
- Prefer 1–2 sentences per statement. This bar is for ordinary cause planks and uniques. Modified/bridge wording may be longer when the extra words are load-bearing — see bridge guidance if this task is mediation.

Expand Down
2 changes: 2 additions & 0 deletions coherence-badge-worker/eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import js from '@eslint/js';
import codeMetrics from '../eslint.metrics.mjs';
import globals from 'globals';
import tseslint from 'typescript-eslint';

export default tseslint.config(
...codeMetrics,
{ ignores: ['dist/**'] },
js.configs.recommended,
...tseslint.configs.recommended,
Expand Down
44 changes: 44 additions & 0 deletions continuity/2026-08-27-statement-generation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Handoff — statement generation (2026-08-27)

Next session: curriculum **exercise 2** (one left/right compromise-in-the-middle triple). Nested-place rollup is **settled** as board inclusion, not implication. Do not polish Christian/secular as the implication demo. Do not load `statement-generation-exercises/` via `loadSeedCollections`.

## Suggested skills

- None required. Read repo docs, not a coding-agent skill. If the next session is a design-doc loop for bridges, `/design`. If implementing a PR plan, `/execute-plan`.

## Read first

1. [`fake-data-generation/statement-generation.md`](../fake-data-generation/statement-generation.md) — process, curriculum, geographic parents, gold-set rules.
2. [`specs/product/statements-are-peculiar-for-good-reasons.md`](../specs/product/statements-are-peculiar-for-good-reasons.md) — why wording is finicky.
3. [`fake-data-generation/seed-content/simple-causes.json`](../fake-data-generation/seed-content/simple-causes.json) — live copy of accepted simple-cause texts.
4. [`cause-assist/src/statementGuidance.ts`](../cause-assist/src/statementGuidance.ts) — same rules for `/atomize` / `/sharpen-plank`.
5. [`fake-data-generation/christian-secular-tiny-seed.md`](../fake-data-generation/christian-secular-tiny-seed.md) — that pairing is a **weak** first implication exercise; do not train generation on it.

## What happened

Goal: a reliable way for an LLM to generate viable seed statements (and cause-assist suggestions), not more hand-wordsmithing.

Wrote the process doc. Ran exercise 1 (simple causes, **no triples**) through live cause-assist, then Adam iterated in-JSON notes. Failures that became generation rules:

| Reject | Rule |
|---|---|
| “X is a public good” / “legitimate way to keep X available” | Want the outcome, do not classify it. |
| “I want maintainers paid” / unpaid nights | Paying is the system. Align projects with the work-product. |
| Category-only OSS | Earmark grain is a **ladder** (OSS → Linux → Linux desktop). |
| Food only by mechanism | Food also tightens by **place** (CSA in Grey County, Ontario). |
| Closed `any` combinator over counties | Nested-place rollup is board inclusion (relevant areas + `within`), not child → parent implication. |

Adam (2026-08-27): those exercise-1 statements **feel viable** to sign and to attest project alignment against. The list is **not** claimed complete for every variation type.

Later same day: copied into [`seed-content/simple-causes.json`](../fake-data-generation/seed-content/simple-causes.json). Same evening: product spec settled nested-place as board inclusion. Ontario farmers-market plank restored `more` (no longer a workaround parent). Garden seed publishes Grey County relevant areas; local-food roster publishes `within: Ontario, Canada`. Tiny seed still aligns the garden to the explorer slogan CID. `loadSeedCollections` does not read `statement-generation-exercises/`. Nested-place pairs are designed-no: `npm run gen:seed:simple-causes-implications`.

Docker cause-assist may still serve the **old** prompt until that service is rebuilt; source guidance is updated.

## Next work

1. ~~Copy accepted planks into `seed-content/`~~ **done.**
2. ~~Geographic rollup Ask~~ **settled** as board inclusion. Seed wording, cause-assist guidance, and the implication attester prompt reject nested-place rollup. Leftover: live-refresh `seed-implication-evaluations` (old fingerprint; v4-flash empty-completion stall).
3. Curriculum **exercise 2**: draft is [`statement-generation-exercises/02-compromise-abortion.json`](../fake-data-generation/statement-generation-exercises/02-compromise-abortion.json) (canonical patterns-page texts). **Not** in seed-content. Run attester + `/critique-triple`. Likely containment gap: modified-right vs 12–16 weeks. If wording changes, change hidden-majority-patterns.md too.
4. **Exercise 3**: gate cause-assist suggestions on the same checks.

Human role remains: pick topics/patterns, veto. Do not silently load exercises into the live corpus. Do not mint geo `any`s. Do not teach Grey → Ontario as implication.
Loading
Loading