feat(packs): 4 bundled lens pack manifests + registry wiring (v0.41 T4)

Authors gbrain-creator + gbrain-investor + gbrain-engineer +
gbrain-everything as bundled YAML manifests in
src/core/schema-pack/base/, registers them in the BUNDLED array in
load-active.ts, exports AGGREGATOR_KINDS + AggregatorKind +
CalibrationDomain types through the schema-pack barrel.

gbrain-creator: atom (NEW page type) + concept (reuse from base).
  phases: [extract_atoms, synthesize_concepts]. One calibration
  domain: concept_themes / cluster_summary / [concept]. Retires
  wintermute's atom-pipeline-coordinator cron (T12 follow-up).

gbrain-investor: thesis + bet_resolution_log (NEW). Borrows
  deal/person/company/yc from base. No new cycle phases (consumes
  existing extract_facts/propose_takes/grade_takes pipeline). Three
  calibration domains: deal_success/scalar_brier/[deal],
  founder_evaluation/scalar_brier/[person], market_call/weighted_brier
  /[thesis]. Filing rules mirror wintermute's existing investing/deals
  + investing/theses + investing/bets layout.

gbrain-engineer: bridge-only per D8-C. ONLY declares `learning`
  page type (primitive: annotation); borrows code+project from base.
  No new cycle phases (gstack-learnings IngestionSource is daemon-
  side per T8). Three calibration domains: architecture_calls/
  scalar_brier/[code, learning], effort_estimates/weighted_brier/
  [project], risk_assessment/scalar_brier/[project].

gbrain-everything: meta-pack extending gbrain-investor + borrowing
  atom (from creator) + learning (from engineer). Codex outside-voice
  T4 resolution to the multi-lens problem: composes via the v0.38-
  shipped extends + borrow_from chain instead of inventing an
  active-multi-pack architecture. Single-active-pack constraint
  preserved. Explicitly re-declares phases + calibration_domains
  (borrow_from borrows types/link_types only — phases must be
  declared per pack per D4-B).

Frontmatter validators (atom_type closed 11-value enum, virality_
score range, etc.) are NOT declared in these manifests — that
contract surface (per-page-type frontmatter_validators on
PageTypeSchema) is a v0.42 follow-up filed in plan TODOs. For
v0.41, extract_atoms hardcodes the enum with a TODO comment
pointing at the eventual manifest read path (D11).

YAML parser caveat: src/core/schema-pack/loader.ts uses a hand-
rolled parseYamlMini (per loader.ts:86 explicit non-support of `|`
block scalars). Initial descriptions used `|` blocks and broke
parsing silently (description was 'literal "|"', everything after
collapsed). Reauthored to single-line "..." strings. Pinned by
the manifest-load tests asserting page_types/phases/calibration_
domains all resolve.

Tests:
  test/lens-pack-manifests.test.ts (31 cases): one file covers all
  4 packs to avoid 4x boilerplate. Pins parse cleanly, registry
  inclusion, per-pack page_types/phases/calibration_domains/filing_
  rules shape, every aggregator value falls in AGGREGATOR_KINDS,
  meta-pack unions correctly (7 calibration domains across all
  three lens packs).

Plan: ~/.claude/plans/system-instruction-you-are-working-toasty-milner.md
Task T4 of 13. Unblocks T5/T6 (phases now declared; phases read
from active pack at runtime), T7 (importer writes atom-typed
pages against creator manifest), T8 (gstack-learnings emits
learning-typed pages against engineer manifest), T9 (orchestrator
gate reads phases: declaration), T10 (calibration_profile walks
calibration_domains).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-05-24 00:07:08 -07:00
co-authored by Claude Opus 4.7
parent f4b2648ba0
commit cefaad31fa
7 changed files with 747 additions and 1 deletions
@@ -0,0 +1,97 @@
# gbrain-creator — content-creator lens pack
#
# v0.41 lens packs wave. Declares the atom + concept lifecycle that the
# downstream wintermute agent ran via its own crons pre-v0.41:
#
# - `atom` page type (NEW to base v0.40 — first introduced here):
# single-source extractive primitive from transcripts/meetings/articles.
# The `extract_atoms` cycle phase produces these via a Haiku 3-check
# quality gate (truism reject, punchline test, entity-page test).
#
# - `concept` page type (already in gbrain-base seed): the synthesize_concepts
# phase aggregates atoms by topic cluster, tiers T1-T4 by composite_score,
# and writes voice-gated T1 Canon narratives via Sonnet.
#
# Cron consolidation: this pack's phases run inside the standard
# `gbrain dream` cycle. No separate wintermute-side cron required.
#
# Calibration: declares `concept_themes` domain using the `cluster_summary`
# aggregator (descriptive rollup, not Brier — concepts don't have binary
# outcomes to score against). When this pack is active OR the gbrain-everything
# meta-pack is active, calibration_profile produces a concept_themes scorecard
# with tier counts and dominant topics.
#
# Frontmatter validators (atom_type closed 11-value enum, virality_score
# 0-100, emotional_register, tweetable, sensitivity, lesson, source_quote,
# draft_angles JSONB) are enforced today INSIDE extract_atoms.ts and read
# from this manifest at runtime via D11. A richer per-page-type
# frontmatter_validators contract on PageTypeSchema is a v0.42 follow-up
# (filed in TODOS.md). For v0.41, the enum lives in code with a comment
# pointing at the eventual manifest surface.
api_version: gbrain-schema-pack-v1
name: gbrain-creator
version: 1.0.0
description: "Content-creator lens. Atoms (single-source extractive nuggets) and concepts (aggregated themes T1-T4). Drives extract_atoms + synthesize_concepts cycle phases. When active, every gbrain dream tick walks recent transcripts/meetings/articles and produces tweetable atoms + tier-promoted concepts. Retires wintermute's parallel atom-pipeline-coordinator cron."
gbrain_min_version: 0.41.0
extends: gbrain-base
borrow_from: []
# Atom + concept inherit takes_kinds from gbrain-base (fact, take, bet, hunch).
# Creator-pack-specific kinds (e.g. `extracted`) are deferred — atoms are not
# claims, they're evidentiary extractions; concepts aggregate atoms not takes.
# takes_kinds intentionally omitted (inherits from base).
# Pack-specific page types. `concept` already exists in gbrain-base seed; we
# don't re-declare it here (the registry merges parent + child page_types).
# `atom` is net-new to base.
page_types:
- name: atom
primitive: concept
path_prefixes:
- atoms/
aliases: []
# extractable: false — atoms ARE the extraction output, not a source for
# further fact extraction. extract_facts skips them. extract_atoms skips
# them too (imported_from frontmatter marker handles the greenfield case;
# the type itself signals "leaf node, no further derivation").
extractable: false
# expert_routing: false — atoms don't surface in whoknows/find_experts.
# Concepts (parent type from base) carry the routing weight for content
# discovery; atoms are content-creator-internal raw material.
expert_routing: false
# v0.41 T3: phase participation. The runCycle orchestrator gates pack-
# flavored phases on this list. Pre-existing 17 core phases ALWAYS run
# regardless of this declaration — `phases:` is additive, not subtractive.
phases:
- extract_atoms
- synthesize_concepts
# v0.41 T3: calibration domain declarations. Each entry binds a per-pack
# scorecard bucket to one of the 4 v1 aggregator kinds. Domain names are
# open strings (third-party packs add new domains without a gbrain release);
# aggregator algorithms are the closed AggregatorKind enum (safe SQL stays
# in code per T3 codex refinement of D6).
calibration_domains:
- name: concept_themes
aggregator: cluster_summary
page_types:
- concept
# Filing rules — date-bucketed atoms (mirrors wintermute's existing layout
# at ~/git/brain/atoms/{YYYY-MM-DD}/{slug}.md so greenfield migration lands
# pages in their canonical locations). Concepts flat-filed per base seed.
filing_rules:
- kind: atom
directory: atoms/
examples:
- atoms/2026-05-24/renders-vs-physical-proof
- atoms/2026-05-24/ai-compliance-headwinds
description: "Single-source extractive nuggets. Date-bucketed by extraction date (NOT source date — multiple atoms from one meeting all land in the day they were extracted). extract_atoms cycle phase writes here."
- kind: concept
directory: concepts/
examples:
- concepts/ai-agents
- concepts/founder-psychology
description: "Aggregated themes T1-T4 by composite_score (mention_count × distinct_months × breadth). synthesize_concepts writes here. T1 Canon concepts carry Sonnet-synthesized narratives gated by the same voice_gate as calibration_profile."
@@ -0,0 +1,113 @@
# gbrain-engineer — engineer / code-author lens pack
#
# v0.41 lens packs wave. Bridge-only pack (D8-C — locked at plan time
# after assessing what already exists in gstack):
#
# - gstack already ships review, investigate, plan-eng-review, health,
# qa, retro, learn, cso skills with a typed JSONL learnings system
# at ~/.gstack/projects/{repo}/learnings.jsonl (pattern, pitfall,
# preference, architecture, tool, operational, investigation).
# - gbrain already ships `type: code` + code-def/code-refs/code-callers.
# - gbrain already ships `type: project` + brain/projects/ directory.
#
# What's missing: the gstack learnings JSONL never reaches gbrain. T8
# fixes that with an IngestionSource bridge that watches the JSONL files
# and emits IngestionEvent per line typed as `learning` page.
#
# Scope deliberately small: declares the `learning` page type (NEW) and
# reuses `code` from gbrain-base. Drops the speculative ADR / postmortem /
# refactor_thesis / tech_debt types from the original plan because:
# (a) gstack's investigation + retro skills already capture postmortems
# and architecture decisions inline (no dedicated type needed)
# (b) shipping types nobody authors is worse than no types at all
# (c) v0.42+ can add them when first user authors one
#
# Calibration: declares 3 engineering domains (architecture_calls,
# effort_estimates, risk_assessment). All scalar_brier except
# effort_estimates which uses weighted_brier — small estimates that are
# wrong cost less than big estimates that are wrong.
#
# Does NOT declare new cycle phases. Engineering work consumes the
# existing extract_facts + propose_takes + grade_takes + calibration_profile
# loop, augmented by the gstack-learnings IngestionSource (T8) running in
# the v0.38 daemon when this pack is active.
api_version: gbrain-schema-pack-v1
name: gbrain-engineer
version: 1.0.0
description: "Engineer / code-author lens. Bridges gstack's typed JSONL learnings system (pattern, pitfall, architecture, tool, operational, investigation) into gbrain as first-class `learning` pages. Declares architecture_calls, effort_estimates, risk_assessment calibration domains. Reuses gbrain-base's `code` page type unchanged."
gbrain_min_version: 0.41.0
extends: gbrain-base
# Reuse `code` + `project` from base. The gstack-learnings bridge writes
# `learning` pages cross-referenced to code:// URIs via link_types, but
# we don't introduce new code-related page types here.
borrow_from:
- pack: gbrain-base
types:
- code
- project
# Engineering uses the same takes_kinds as base (fact, take, bet, hunch).
# `bet` is the load-bearing one for architecture_calls (a bet on a
# refactor's payoff is a take).
# takes_kinds intentionally omitted (inherits from base).
# Pack-specific page types. ONLY `learning` is new (D8-C: bridge-only).
page_types:
- name: learning
primitive: annotation
path_prefixes:
- learnings/
- engineering/learnings/
aliases: []
# extractable: false — learnings ARE distilled engineering insights;
# they're not raw source material for further fact extraction.
extractable: false
# expert_routing: false — learnings surface in code-aware queries
# via direct slug search, not via whoknows/find_experts (whoknows is
# person+company shaped, learnings are pattern shaped).
expert_routing: false
# No new cycle phases. Engineering consumes existing pipeline.
# gstack-learnings IngestionSource (T8) emits events when this pack is
# active — handled by the v0.38 daemon's normal trickle-mode dispatch.
phases: []
# v0.41 T3: engineering-flavored calibration domains.
calibration_domains:
- name: architecture_calls
# "How often I'm wrong about which architecture to pick" — scalar
# Brier over resolved code-attached + learning-attached binary takes
# (e.g. "this refactor was worth it" resolved true/false).
aggregator: scalar_brier
page_types:
- code
- learning
- name: effort_estimates
# Weighted Brier — a 1-day-estimate miss costs less than a 1-quarter
# estimate miss. Weight by take.confidence so high-conviction
# estimates that fail count more.
aggregator: weighted_brier
page_types:
- project
- name: risk_assessment
# "How often I correctly flag a risk" — scalar Brier over project-
# attached binary takes (e.g. "this rollout will OOM" resolved
# true/false). Distinct from architecture_calls (those are go/no-go
# decisions on direction; risk_assessment is about specific failure
# mode predictions).
aggregator: scalar_brier
page_types:
- project
# Filing rules.
filing_rules:
- kind: learning
directory: learnings/
examples:
- learnings/2026-05/orbstack-port-8080-conflict
- learnings/2026-05/django-form-cleaned-data-defaults-empty
description: "Engineering learnings bridged from gstack's ~/.gstack/projects/{repo}/learnings.jsonl. Per-month subdirectories. Frontmatter carries the original JSONL fields verbatim: learning_type (one of: pattern, pitfall, preference, architecture, tool, operational, investigation), confidence (1-10), source (one of: observed, user-stated, inferred, cross-model), files (paths), skill (originating gstack skill name)."
@@ -0,0 +1,121 @@
# gbrain-everything — meta-pack stacking all three lenses
#
# v0.41 lens packs wave. Codex outside-voice T4 resolution: gbrain's
# 7-tier active-pack resolver is single-valued, but the user's actual
# workflow needs creator + investor + engineer simultaneously (Garry
# switches between YC investor work, content creation, and gstack
# engineering across the same day). Two paths to "three lenses on one
# brain":
#
# A) New active-multi-pack composition concept. New code, new
# architecture, new corners. Deferred per plan's NOT-in-scope.
#
# B) Compose via the v0.38-shipped extends chain. Author a meta-pack
# that extends one of the three, and bring the other two via
# borrow_from. registry walks the chain, unions page_types +
# link_types + phases + calibration_domains. ZERO new architecture.
#
# This pack is path B. Activate via `gbrain config set schema_pack
# gbrain-everything` and the brain runs all three lens-pack phases
# (extract_atoms + synthesize_concepts from creator; gstack-learnings
# bridge from engineer) AND surfaces all 7 calibration domains
# (_overall + concept_themes + deal_success + founder_evaluation +
# market_call + architecture_calls + effort_estimates + risk_assessment).
#
# Extension chain (registry resolves via extends.parent.parent.parent):
#
# gbrain-everything ──extends──▶ gbrain-investor ──extends──▶ gbrain-base
# │ borrow │
# ├─ gbrain-creator
# └─ gbrain-engineer
#
# Single-active-pack constraint preserved: this IS the active pack.
# Registry walks its dependencies through extends + borrow_from to
# materialize the merged view. Tested by the everything-manifest test.
api_version: gbrain-schema-pack-v1
name: gbrain-everything
version: 1.0.0
description: "Meta-pack stacking creator + investor + engineer lenses on one brain. Use this when you do investor work AND content creation AND engineering in the same brain (YC president pattern, founder pattern). Single active-pack activation via `gbrain config set schema_pack gbrain-everything` — registry walks extends + borrow_from chain to union all three lenses' page_types, phases, and calibration_domains."
gbrain_min_version: 0.41.0
# Extends gbrain-investor (which itself extends gbrain-base) and borrows
# from gbrain-creator + gbrain-engineer. The registry's extends chain
# walker (cap depth 8) unions every level's contributions.
extends: gbrain-investor
# Borrow types + link_types from the other two lens packs. This is the
# composition mechanism that makes "three lenses" real without a
# multi-active-pack architecture.
borrow_from:
- pack: gbrain-creator
types:
- atom
# concept already in base
- pack: gbrain-engineer
types:
- learning
# code + project already in base
# No new page types declared here — everything comes via extends + borrow.
page_types: []
# Union of creator + engineer phase declarations. Investor pack declares
# no phases (consumes existing pipeline), so nothing inherited from the
# extends chain there. Creator's phases must be re-declared explicitly
# because `borrow_from` borrows types/link_types only, NOT phases (D4-B
# decision: each pack declares its phase participation explicitly).
#
# Engineer pack's phases: [] is the empty case — the gstack-learnings
# IngestionSource is daemon-side, not a cycle phase.
phases:
- extract_atoms
- synthesize_concepts
# Union of creator + investor + engineer calibration domains. Same
# explicit re-declaration rationale as phases — calibration_domains
# aren't borrowed; the meta-pack declares the full set.
#
# Aggregator algorithms are the closed enum; domain names match the
# per-pack declarations so the JSONB scorecard keys are stable across
# active-pack switches (a user moving from gbrain-investor to
# gbrain-everything keeps their deal_success scorecard intact).
calibration_domains:
# From gbrain-creator:
- name: concept_themes
aggregator: cluster_summary
page_types:
- concept
# From gbrain-investor:
- name: deal_success
aggregator: scalar_brier
page_types:
- deal
- name: founder_evaluation
aggregator: scalar_brier
page_types:
- person
- name: market_call
aggregator: weighted_brier
page_types:
- thesis
# From gbrain-engineer:
- name: architecture_calls
aggregator: scalar_brier
page_types:
- code
- learning
- name: effort_estimates
aggregator: weighted_brier
page_types:
- project
- name: risk_assessment
aggregator: scalar_brier
page_types:
- project
# Filing rules inherit through extends + borrow. No re-declaration needed —
# registry merges parent + borrowed pack filing_rules.
filing_rules: []
@@ -0,0 +1,151 @@
# gbrain-investor — investor/YC lens pack
#
# v0.41 lens packs wave. Declares the investor work surface that the user
# (YC president) already runs every day across the wintermute agent + brain:
# - 501 investor profiles at brain/investors/
# - 19 deal memos at brain/investing/deals/
# - 5.3K companies + 24.6K people + 7.1K YC pages
# - 8 investor skills (yc-radar, yc-portfolio-health, sequoia-batch-bangers,
# investor-diligence, batch-picks-for-investor, etc.)
# - Fences already in use: ## Recommendation, ## Pre-mortem, ## Defensibility
# (6-8 moats), ## Founders, ## Product, ## Market / Customers, ## Traction,
# ## Team, ## Signals
#
# This pack consolidates the EXISTING primitives as pack-owned + adds a
# bet-resolution lifecycle:
#
# - `thesis` (NEW): investment thesis with thesis_text, key_bets[],
# market_view, vintage. Filed at investing/theses/{slug}.
#
# - `bet_resolution_log` (NEW): outcome record for a thesis's bet. Bound
# to a take row via take_id, carries resolved_outcome + resolved_at +
# learned_pattern. Filed at investing/bets/{YYYY-MM}/{slug}.
#
# Calibration: declares 3 domains feeding the v0.41-widened
# calibration_profile: deal_success (Brier over resolved deal-flagged
# takes), founder_evaluation (Brier over person-flagged takes), market_call
# (weighted Brier over thesis-flagged takes, weighted by take.confidence
# because market calls' high-conviction-but-rare nature wants Brier scaled
# by stake).
#
# Does NOT declare new cycle phases — investor work consumes the existing
# extract_facts + propose_takes + grade_takes + calibration_profile loop.
# The propose_takes phase populates take_domain_assignments rows at write
# time from this pack's page_types → domain mapping (T10 wiring).
#
# Cron consolidation: existing wintermute crons (yc-radar-weekly,
# yc-portfolio-health monthly, yc-oh-ingest daily) continue running on
# their own schedules in wintermute — gbrain-investor doesn't displace
# them, only declares their output types + calibration semantics. v0.42+
# may migrate those crons too once their phase contracts stabilize.
api_version: gbrain-schema-pack-v1
name: gbrain-investor
version: 1.0.0
description: "Investor / YC president lens. Adds thesis + bet_resolution_log types to the deal/investor/company/person surface that already exists in gbrain-base. Declares deal_success / founder_evaluation / market_call calibration domains feeding the widened calibration_profile."
gbrain_min_version: 0.41.0
extends: gbrain-base
# Borrow deal + person + company + yc from gbrain-base — they remain
# base-owned (other packs may also use them) but this pack's calibration
# domains reference them. No new declarations needed because borrow_from
# just signals intent; the registry already merges types from the
# extends chain.
borrow_from:
- pack: gbrain-base
types:
- deal
- person
- company
- yc
# Investor work uses the same takes_kinds as base (fact, take, bet, hunch).
# The `bet` kind is the load-bearing one for this pack — bet_resolution_log
# tracks outcomes for take rows of kind='bet'.
# takes_kinds intentionally omitted (inherits from base).
# Pack-specific page types. `deal` already exists in gbrain-base seed.
# `thesis` and `bet_resolution_log` are net-new for v0.41.
#
# `investor` and `founder` (alias for person in deal context) are
# deferred: investor pages are filed under investors/ but the type is
# `person` today and changing it requires data migration. v0.42+ can
# introduce them with proper migration.
page_types:
- name: thesis
primitive: concept
path_prefixes:
- investing/theses/
- theses/
aliases: []
# extractable: true — theses contain claims that the LLM extractor
# can mine into facts (the thesis itself is a claim; sub-bets are
# claims). extract_facts walks them.
extractable: true
# expert_routing: false — theses don't surface in find_experts;
# founders + companies carry that weight.
expert_routing: false
- name: bet_resolution_log
primitive: temporal
path_prefixes:
- investing/bets/
- bets/
aliases: []
extractable: false
expert_routing: false
# No new cycle phases. Investor work consumes the existing pipeline.
phases: []
# v0.41 T3: investor-flavored calibration domains. Each binds the closed
# aggregator algorithm to the open domain name + page_types whose takes
# feed this bucket. propose_takes (T10) populates take_domain_assignments
# rows at write time from this mapping when this pack is active.
calibration_domains:
- name: deal_success
# Standard Brier over resolved deal-attached binary takes
# (did this deal succeed? = take.resolved_outcome).
aggregator: scalar_brier
page_types:
- deal
- name: founder_evaluation
# Standard Brier over person-attached takes. The "how often I'm
# wrong about founders" signal.
aggregator: scalar_brier
page_types:
- person
- name: market_call
# Weighted Brier — market calls are rare-but-high-stakes; weighting
# by take.confidence means a misfired strong conviction costs more
# than a misfired weak hunch.
aggregator: weighted_brier
page_types:
- thesis
# Filing rules — mirror wintermute's existing layout so the v0.41
# greenfield migration (T7) lands pages in their canonical locations.
filing_rules:
- kind: deal
directory: investing/deals/
examples:
- investing/deals/widget-co-pre-a-memo
description: "Investment memos. Pre-existing fences (## Recommendation, ## Pre-mortem, ## Defensibility, ## Founders, ## Product, ## Market / Customers, ## Traction, ## Team) carry through unchanged."
- kind: thesis
directory: investing/theses/
examples:
- investing/theses/ai-agents-2026
- investing/theses/hardware-renaissance
description: "Investment theses with thesis_text + key_bets[] + market_view + vintage frontmatter. Sub-bets resolve into bet_resolution_log entries via the take_id FK chain."
- kind: bet_resolution_log
directory: investing/bets/
examples:
- investing/bets/2026-04/widget-co-arr-trajectory
description: "Resolution records for thesis bets. Month-bucketed by resolution date. Frontmatter: bet_id (FK to thesis.key_bets), resolved_outcome (boolean), resolved_at (ISO date), learned_pattern (one-sentence takeaway). Populates the market_call calibration domain."
- kind: investor
directory: investors/
examples:
- investors/example-vc-fund
description: "Investor profiles (501 already extant). Filed flat under investors/. Page type stays `person` for now (data-migration deferral). v0.42+ may introduce dedicated `investor` page type."
+4
View File
@@ -16,6 +16,10 @@ export {
parseSchemaPackManifest, parseSchemaPackManifest,
computeManifestSha8, computeManifestSha8,
packIdentity, packIdentity,
// v0.41 T3 — calibration domain registry
AGGREGATOR_KINDS,
type AggregatorKind,
type CalibrationDomain,
} from './manifest-v1.ts'; } from './manifest-v1.ts';
export { export {
+14 -1
View File
@@ -95,7 +95,20 @@ function defaultPackLocator(name: string): string | null {
// v0.39 T8 — bundled packs registry. gbrain-base + gbrain-recommended // v0.39 T8 — bundled packs registry. gbrain-base + gbrain-recommended
// ship in src/core/schema-pack/base/. Add a new entry here to bundle // ship in src/core/schema-pack/base/. Add a new entry here to bundle
// additional canonical packs. // additional canonical packs.
const BUNDLED: ReadonlyArray<string> = ['gbrain-base', 'gbrain-recommended']; //
// v0.41 T4 — lens packs join the bundle: creator (atoms + concepts +
// extract_atoms/synthesize_concepts phases), investor (theses + bet
// resolution + 3 calibration domains), engineer (gstack-learnings bridge
// + 3 calibration domains), everything (meta-pack stacking all three
// via extends + borrow_from). Each ships as a real YAML at base/<name>.yaml.
const BUNDLED: ReadonlyArray<string> = [
'gbrain-base',
'gbrain-recommended',
'gbrain-creator',
'gbrain-investor',
'gbrain-engineer',
'gbrain-everything',
];
if (BUNDLED.includes(name)) { if (BUNDLED.includes(name)) {
// Resolve bundled YAML relative to this source file. Works in both // Resolve bundled YAML relative to this source file. Works in both
// direct-bun execution and bun --compile binaries. // direct-bun execution and bun --compile binaries.
+247
View File
@@ -0,0 +1,247 @@
// v0.41 T4 — bundled lens pack manifest smoke tests.
//
// One test file covers all 4 packs (creator, investor, engineer, everything)
// because each test boils down to "manifest parses + declares the expected
// shape." Splitting per-pack would 4x the boilerplate without adding signal.
//
// Pinned contracts:
// - All 4 YAMLs parse via parseSchemaPackManifest without error
// - Each pack registered in BUNDLED (loadPackManifestByName resolves)
// - Each pack declares the expected page_types, phases, calibration_domains
// - extends chain resolves through registry without depth error
// - gbrain-everything unions all three lens packs' contributions
// - Calibration domain aggregator is the closed AggregatorKind enum on every entry
import { describe, test, expect } from 'bun:test';
import { readFileSync, existsSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
import {
parseSchemaPackManifest,
parseYamlMini,
AGGREGATOR_KINDS,
type SchemaPackManifest,
} from '../src/core/schema-pack/index.ts';
const PACK_NAMES = [
'gbrain-creator',
'gbrain-investor',
'gbrain-engineer',
'gbrain-everything',
] as const;
const here = dirname(fileURLToPath(import.meta.url));
const baseDir = join(here, '..', 'src', 'core', 'schema-pack', 'base');
function loadPack(name: string): SchemaPackManifest {
const p = join(baseDir, `${name}.yaml`);
if (!existsSync(p)) {
throw new Error(`bundled pack not found at ${p}`);
}
const raw = readFileSync(p, 'utf-8');
const parsed = parseYamlMini(raw);
return parseSchemaPackManifest(parsed, { path: p });
}
describe('v0.41 T4: all 4 bundled lens packs parse cleanly', () => {
for (const name of PACK_NAMES) {
test(`${name}.yaml parses via parseSchemaPackManifest without error`, () => {
const pack = loadPack(name);
expect(pack.name).toBe(name);
expect(pack.version).toMatch(/^\d+\.\d+\.\d+$/);
expect(pack.api_version).toBe('gbrain-schema-pack-v1');
});
}
});
describe('v0.41 T4: bundled registry includes lens packs', () => {
test('load-active.ts BUNDLED array source includes the 4 lens pack names', () => {
const loadActiveSrc = readFileSync(
join(here, '..', 'src', 'core', 'schema-pack', 'load-active.ts'),
'utf-8',
);
for (const name of PACK_NAMES) {
expect(loadActiveSrc).toContain(`'${name}'`);
}
});
});
describe('v0.41 T4: gbrain-creator manifest shape', () => {
const pack = loadPack('gbrain-creator');
test('extends gbrain-base', () => {
expect(pack.extends).toBe('gbrain-base');
});
test('declares atom page type (NEW to base)', () => {
const atom = pack.page_types.find((p) => p.name === 'atom');
expect(atom).toBeDefined();
expect(atom?.primitive).toBe('concept');
expect(atom?.path_prefixes).toContain('atoms/');
expect(atom?.extractable).toBe(false); // leaf node, not source for further extraction
expect(atom?.expert_routing).toBe(false);
});
test('declares extract_atoms + synthesize_concepts phases', () => {
expect(pack.phases).toContain('extract_atoms');
expect(pack.phases).toContain('synthesize_concepts');
});
test('declares concept_themes calibration domain with cluster_summary aggregator', () => {
const themes = pack.calibration_domains.find((d) => d.name === 'concept_themes');
expect(themes).toBeDefined();
expect(themes?.aggregator).toBe('cluster_summary');
expect(themes?.page_types).toContain('concept');
});
test('filing rules for atom + concept include canonical paths', () => {
const atomRule = pack.filing_rules.find((r) => r.kind === 'atom');
expect(atomRule?.directory).toBe('atoms/');
const conceptRule = pack.filing_rules.find((r) => r.kind === 'concept');
expect(conceptRule?.directory).toBe('concepts/');
});
});
describe('v0.41 T4: gbrain-investor manifest shape', () => {
const pack = loadPack('gbrain-investor');
test('extends gbrain-base + borrows deal/person/company/yc', () => {
expect(pack.extends).toBe('gbrain-base');
const borrowEntry = pack.borrow_from.find((b) => b.pack === 'gbrain-base');
expect(borrowEntry).toBeDefined();
expect(borrowEntry?.types).toEqual(expect.arrayContaining(['deal', 'person', 'company', 'yc']));
});
test('declares thesis + bet_resolution_log page types', () => {
const thesis = pack.page_types.find((p) => p.name === 'thesis');
expect(thesis).toBeDefined();
expect(thesis?.primitive).toBe('concept');
expect(thesis?.extractable).toBe(true);
const bet = pack.page_types.find((p) => p.name === 'bet_resolution_log');
expect(bet).toBeDefined();
expect(bet?.primitive).toBe('temporal');
});
test('declares NO new cycle phases (consumes existing pipeline)', () => {
expect(pack.phases).toEqual([]);
});
test('declares 3 calibration domains (deal_success + founder_evaluation + market_call)', () => {
const names = pack.calibration_domains.map((d) => d.name).sort();
expect(names).toEqual(['deal_success', 'founder_evaluation', 'market_call']);
});
test('every calibration_domain aggregator is in the closed AggregatorKind enum', () => {
for (const d of pack.calibration_domains) {
expect(AGGREGATOR_KINDS).toContain(d.aggregator);
}
});
test('market_call uses weighted_brier (high-conviction-rare-event semantics)', () => {
const mc = pack.calibration_domains.find((d) => d.name === 'market_call');
expect(mc?.aggregator).toBe('weighted_brier');
});
test('filing rules cover deal + thesis + bet_resolution_log + investor', () => {
const kinds = pack.filing_rules.map((r) => r.kind).sort();
expect(kinds).toEqual(['bet_resolution_log', 'deal', 'investor', 'thesis']);
});
});
describe('v0.41 T4: gbrain-engineer manifest shape', () => {
const pack = loadPack('gbrain-engineer');
test('extends gbrain-base + borrows code/project', () => {
expect(pack.extends).toBe('gbrain-base');
const borrowEntry = pack.borrow_from.find((b) => b.pack === 'gbrain-base');
expect(borrowEntry?.types).toEqual(expect.arrayContaining(['code', 'project']));
});
test('declares ONLY learning page type (D8-C bridge-only)', () => {
expect(pack.page_types.length).toBe(1);
expect(pack.page_types[0].name).toBe('learning');
expect(pack.page_types[0].primitive).toBe('annotation');
});
test('declares NO new cycle phases (gstack bridge is daemon-side IngestionSource)', () => {
expect(pack.phases).toEqual([]);
});
test('declares 3 calibration domains (architecture_calls + effort_estimates + risk_assessment)', () => {
const names = pack.calibration_domains.map((d) => d.name).sort();
expect(names).toEqual(['architecture_calls', 'effort_estimates', 'risk_assessment']);
});
test('effort_estimates uses weighted_brier (small-vs-big estimate scaling)', () => {
const ee = pack.calibration_domains.find((d) => d.name === 'effort_estimates');
expect(ee?.aggregator).toBe('weighted_brier');
});
test('every calibration_domain aggregator is in the closed AggregatorKind enum', () => {
for (const d of pack.calibration_domains) {
expect(AGGREGATOR_KINDS).toContain(d.aggregator);
}
});
});
describe('v0.41 T4: gbrain-everything meta-pack shape', () => {
const pack = loadPack('gbrain-everything');
test('extends gbrain-investor (chain head)', () => {
expect(pack.extends).toBe('gbrain-investor');
});
test('borrows from gbrain-creator + gbrain-engineer', () => {
const borrowedPacks = pack.borrow_from.map((b) => b.pack).sort();
expect(borrowedPacks).toEqual(['gbrain-creator', 'gbrain-engineer']);
});
test('borrows atom from creator and learning from engineer', () => {
const creatorBorrow = pack.borrow_from.find((b) => b.pack === 'gbrain-creator');
expect(creatorBorrow?.types).toContain('atom');
const engineerBorrow = pack.borrow_from.find((b) => b.pack === 'gbrain-engineer');
expect(engineerBorrow?.types).toContain('learning');
});
test('declares NO own page_types (everything via extends + borrow)', () => {
expect(pack.page_types).toEqual([]);
});
test('explicitly re-declares phases from creator (borrow_from does NOT borrow phases)', () => {
expect(pack.phases).toEqual(['extract_atoms', 'synthesize_concepts']);
});
test('explicitly unions ALL 7 lens calibration domains', () => {
const names = pack.calibration_domains.map((d) => d.name).sort();
expect(names).toEqual([
'architecture_calls',
'concept_themes',
'deal_success',
'effort_estimates',
'founder_evaluation',
'market_call',
'risk_assessment',
]);
});
test('every meta-pack calibration_domain aggregator is in the closed enum', () => {
for (const d of pack.calibration_domains) {
expect(AGGREGATOR_KINDS).toContain(d.aggregator);
}
});
test('aggregator selection matches per-pack declarations (cross-pack consistency)', () => {
const byName = Object.fromEntries(pack.calibration_domains.map((d) => [d.name, d.aggregator]));
// From investor
expect(byName.deal_success).toBe('scalar_brier');
expect(byName.market_call).toBe('weighted_brier');
expect(byName.founder_evaluation).toBe('scalar_brier');
// From creator
expect(byName.concept_themes).toBe('cluster_summary');
// From engineer
expect(byName.architecture_calls).toBe('scalar_brier');
expect(byName.effort_estimates).toBe('weighted_brier');
expect(byName.risk_assessment).toBe('scalar_brier');
});
});