#!/usr/bin/env python3
"""Ingest curriculum and education knowledge into the brain system."""
import sys
import os
from datetime import datetime, timezone
sys.path.insert(0, os.path.expanduser('~/.hermes/brain'))
from brain import create_entity, add_edge
def e(content, entity_id, category, entity_type='curriculum', strength='strong', pinned=False, metadata=None):
"""Helper to create entity, avoiding triple-quote issues."""
return create_entity(
content=content,
entity_id=entity_id,
entity_type=entity_type,
category=category,
strength=strength,
pinned=pinned,
metadata=metadata or {},
)
def ingest():
entities = {}
# 1. CURRICULUM OVERVIEW (pinned)
entities['overview'] = e(
entity_id="domain_curriculum",
category="curriculum",
entity_type="domain",
pinned=True,
content=(
"# 2nd Grade Curriculum Domain\n\n"
"Complete 2nd grade homeschool curriculum: Math, ELA, Science across 32 weeks.\n\n"
"## Structure\n"
"- **Math**: 32 weeks, 160 lessons (Mon-Fri), 5 phases\n"
"- **ELA**: 32 weeks, 160 lessons (Mon-Fri), 5 phases\n"
"- **Science**: 32 weeks, 96 lessons (Mon/Wed/Fri), topic-based\n"
"- **Spelling/Phonics**: Integrated into ELA, separate generator workflow\n\n"
"## Location\n"
"- Base: `~/Home_School/2nd_Grade/`\n"
"- Math: `~/Home_School/2nd_Grade/Math/`\n"
"- ELA: `~/Home_School/2nd_Grade/English_Language_Arts/`\n"
"- Science: `~/Home_School/2nd_Grade/Science/`\n\n"
"## Output Format\n"
"- PDF worksheets via WeasyPrint\n"
"- Strict page constraints per day\n"
"- Purple (#673ab7) and orange (#ff9800) color scheme\n"
"- Sky/fence/ground writing lines\n\n"
"## Key Skills\n"
"- ela-week-generator-architecture, spelling-generator-workflow, generate-math-week\n"
"- worksheet-generator-workflow, math-worksheet-audit, handwriting-practice-specs\n"
"- ela-audit-workflow, curriculum-audit-checklist"
),
metadata={"subject": "overview", "grade": "2nd", "weeks": 32},
)
# 2. MATH CURRICULUM (pinned)
entities['math'] = e(
entity_id="curriculum_math",
category="curriculum/math",
entity_type="curriculum",
pinned=True,
content=(
"# 2nd Grade Math Curriculum\n\n"
"## Phases & Progression\n\n"
"### Phase 1: Number Sense & Foundations (Weeks 1-6)\n"
"- Wk 1-2: Counting & Number Sense to 100, Place Value (Tens & Ones)\n"
"- Wk 3-5: Comparing/Ordering, Addition Facts to 20, Subtraction Facts to 20\n"
"- Wk 6: Phase 1 checkpoint\n\n"
"### Phase 2: Addition & Subtraction to 100 (Weeks 7-14)\n"
"- Wk 7-8: Adding/Subtracting within 100 (no regrouping)\n"
"- Wk 9-10: Addition/Subtraction with Regrouping\n"
"- Wk 11-13: Mixed 2-Digit, Adding 3 Numbers, 3-Digit Add/Sub\n"
"- Wk 14: Word Problems (Add/Sub to 100)\n\n"
"### Phase 3: Measurement, Time & Money (Weeks 15-20)\n"
"- Wk 15-17: Length (inches/cm), Telling Time, Time Word Problems\n"
"- Wk 18-19: Money (coins/bills, adding & change)\n"
"- Wk 20: Phase 3 checkpoint\n\n"
"### Phase 4: Geometry & Fractions (Weeks 21-26)\n"
"- Wk 21-23: 2D Shapes, 3D Shapes, Lines/Angles/Symmetry\n"
"- Wk 24-25: Area & Perimeter, Intro to Fractions\n"
"- Wk 26: Phase 4 Review\n\n"
"### Phase 5: Multiplication, Division & Patterns (Weeks 27-32)\n"
"- Wk 27-28: Equal Groups/Arrays, Repeated Addition -> Multiplication\n"
"- Wk 29-30: Multiplication Facts 0-5, then 6-10\n"
"- Wk 31: Division as Equal Sharing\n"
"- Wk 32: Final Review & Assessment\n\n"
"## Conventions\n"
"- 5 days/week, each day a separate PDF, 6-8 activities per page\n"
"- SVG fraction circles (skill: math-fraction-svg)\n"
"- SVG clock faces (skill: svg-clock-generation)\n"
"- Hundreds charts, skip counting (2s, 5s, 10s)\n\n"
"## Audit\n"
"- `lint_week.py` before generating (catches f-string bugs)\n"
"- `scripts/audit_math.py` for correctness check\n"
"- Validates: skip-counting, place value, ordering, comparison operators"
),
metadata={"subject": "math", "weeks": 32, "phases": 5, "lessons": 160},
)
# 3. ELA CURRICULUM (pinned)
entities['ela'] = e(
entity_id="curriculum_ela",
category="curriculum/ela",
entity_type="curriculum",
pinned=True,
content=(
"# 2nd Grade ELA Curriculum\n\n"
"## Structure\n"
"- Encoding (Spelling/Grammar): Learning to write correctly\n"
"- Decoding (Phonics/Reading): Learning to read correctly\n"
"- 32 weeks, 5 phases, 160 lessons total\n\n"
"## Phonics Phases\n\n"
"### Phase 1: Foundational Patterns (Weeks 1-6)\n"
"- Wk 1: Short Vowels, Wk 2: Silent E, Wk 3: AI/AY/EE/EA\n"
"- Wk 4: OA/OW/OU/EW, Wk 5: Digraphs (SH/CH/TH/WH/PH/CK), Wk 6: Soft C/G\n\n"
"### Phase 2: Advanced Patterns (Weeks 7-12)\n"
"- Wk 7: R-Controlled, Wk 8: Double Consonants\n"
"- Wk 9: Prefixes (UN-/RE-/DIS-), Wk 10: Suffixes (-ED/-ING/-S/-ES)\n"
"- Wk 11: Suffixes (-ER/-EST/-LY/-FUL/-LESS), Wk 12: Homophones\n\n"
"### Phase 3: Word Meaning & Vocabulary (Weeks 13-18)\n"
"- Wk 13: High-Frequency Words, Wk 14-15: Synonyms/Antonyms\n"
"- Wk 16: Multiple Meaning Words, Wk 17: Word Families & Roots\n"
"- Wk 18: Context Clues\n\n"
"### Phase 4: Grammar & Writing (Weeks 19-24)\n"
"- Wk 19: Nouns, Wk 20: Verbs, Wk 21: Adjectives/Adverbs\n"
"- Wk 22: Sentence Structure/Punctuation, Wk 23: Contractions/Apostrophes\n"
"- Wk 24: Figurative Language\n\n"
"### Phase 5: Review & Mastery (Weeks 25-32)\n"
"- Wk 25-26: Cumulative Review (Vowels, Prefixes/Suffixes)\n"
"- Wk 27-28: Challenging Spellings (Y/IE-EI/IGH), Syllabification\n"
"- Wk 29-31: Spelling in Writing, Word Categories, Vocab in Reading\n"
"- Wk 32: Final Review & Celebration\n\n"
"## Word Rules\n"
"- 12 words/week, 6+ letters minimum, ALL match target phonics pattern\n\n"
"## Daily Layout\n"
"- Mon (4p): Word Cards -> Story -> Vowel Sort -> Copy/Fill/Opposites\n"
"- Tue (3p): Scramble -> Reading -> Comprehension -> Pattern Hunt -> Word Swap\n"
"- Wed (4p): Reading -> Vocab Q -> Syn/Ant -> Word Web -> Context Clues\n"
"- Thu (4p): Story -> Detective -> Swap -> Paragraph -> Creative Writing\n"
"- Fri (5p): TEST -> Review Reading -> Opposites -> Journal -> Celebration\n\n"
"## Friday Rules\n"
"- NO learning/remember boxes (assessment only)\n"
"- Spelling test: 12 blank lines"
),
metadata={"subject": "ela", "weeks": 32, "phases": 5, "lessons": 160},
)
# 4. SCIENCE CURRICULUM (pinned)
entities['science'] = e(
entity_id="curriculum_science",
category="curriculum/science",
entity_type="curriculum",
pinned=True,
content=(
"# 2nd Grade Science Curriculum\n\n"
"## Structure\n"
"- 32 weeks, Mon/Wed/Fri schedule (3 lessons/week = 96 total)\n"
"- Topic-based progression with hands-on experiments\n\n"
"## Units\n\n"
"### Scientific Method & Observation (Weeks 1-2)\n"
"- Wk 1: Introduction to Scientific Observation\n"
"- Wk 2: Properties of Materials\n\n"
"### Properties of Matter (Weeks 3-10)\n"
"- Wk 3-5: Hard/Soft/Rough/Smooth, Flexible/Rigid, Absorbent\n"
"- Wk 6-9: Solids/Liquids/Gases intro + deep dives\n"
"- Wk 10: States of Matter Review\n\n"
"### Plants & Growth (Weeks 11-16)\n"
"- Wk 11: What Do Plants Need?\n"
"- Wk 12-14: Plant Growth Experiment (setup, observation)\n"
"- Wk 15: Plant Parts and Functions\n"
"- Wk 16: Experiment Results\n\n"
"### Habitats & Ecosystems (Weeks 17-20)\n"
"- Wk 17-19: Land Habitats, Water Habitats\n"
"- Wk 20: Ecosystems and Interdependence\n\n"
"### Earth Science (Weeks 21-27)\n"
"- Wk 21-22: Water on Earth, Water Cycle\n"
"- Wk 23-25: Weather/Climate, Slow Changes, Erosion/Weathering\n"
"- Wk 26-27: Fast Changes, Rocks and Soil\n\n"
"### Biology & Engineering (Weeks 28-32)\n"
"- Wk 28-29: Diversity of Plants/Animals\n"
"- Wk 30: Patterns in Nature\n"
"- Wk 31: Engineering Design Challenge\n"
"- Wk 32: Year-End Review & Celebration\n\n"
"## Pedagogy\n"
"- Hands-on experiments, observation journals\n"
"- Engineering design challenges, real-world connections"
),
metadata={"subject": "science", "weeks": 32, "lessons": 96},
)
# 5. WORKSHEET DESIGN CONVENTIONS (pinned)
entities['design'] = e(
entity_id="curriculum_worksheet_design",
category="curriculum/design",
entity_type="convention",
pinned=True,
content=(
"# Worksheet Design Conventions\n\n"
"## Layout\n"
"- Page: 8.5x11\" (letter), Margins: 0.5\" (36pt) left/right\n"
"- Colors: Purple (#673ab7), Orange (#ff9800)\n"
"- Blanks: 4-6em short, 6-8em shapes, 8em+ explanations\n"
"- CSS: .b=3em, .bw=6em, .bl=8em\n"
"- Shapes: horizontal flex row (display:flex; gap:10px), NOT grids\n\n"
"## Writing Lines (Sky/Fence/Ground)\n"
"- Blue solid = ground (baseline), Red dashed = fence (midline)\n"
"- Blue dotted = sky (topline)\n"
"- In tables: requires position:relative on parent td\n"
"- Use write_line() helper from helpers.py\n\n"
"## Page Breaks\n"
"- Explicit breaks between sections via page_break() helper\n"
"- Never let content bleed across page boundaries\n"
"- Fix overflow with height spacers or explicit breaks\n\n"
"## Friday Assessments\n"
"- NO learning/remember boxes, clean test format\n\n"
"## Helper Functions (from helpers.py)\n"
"- CSS, header, sectitle, secnote, write_line, copy_line\n"
"- page_break, word_bank, fill_blank_with_line, word_search\n"
"- remember_box, fb\n\n"
"## Validation\n"
"- from validate import validate_week, report_validation"
),
metadata={"type": "design-convention", "applies_to": "all_subjects"},
)
# 6. GENERATOR ARCHITECTURE
entities['generators'] = e(
entity_id="curriculum_generator_architecture",
category="curriculum/generators",
entity_type="architecture",
content=(
"# Worksheet Generator Architecture\n\n"
"## File Structure\n"
"```\n"
"~/Home_School/2nd_Grade/English_Language_Arts/\n"
" generate_week_template.py # Master template with [UPDATE] markers\n"
" validate.py # Shared validation\n"
" helpers.py # Shared CSS + HTML helpers\n"
" Week_XX/generate_weekX.py # Week-specific generator\n"
"```\n\n"
"## Workflow\n"
"1. Copy template -> Week_XX/generate_weekX.py\n"
"2. Change WEEK path, fill WORDS list (word, pattern, definition)\n"
"3. Replace 73 [UPDATE] markers with week-specific content\n"
"4. Run: python3 Week_XX/generate_weekX.py\n\n"
"## Best Practice\n"
"- DO: Build incrementally with execute_code, validate syntax per step\n"
"- DO NOT: Use patch on generators (fuzzy-match issues with HTML tags)\n"
"- Pattern: Build -> compile(full, file, exec) -> write\n\n"
"## Pre-Generation\n"
"- lint_week.py (catches f-string bugs)\n"
"- Delete stale PDFs: rm -f Week_XX/*/Week*.pdf\n"
"- Verify 12 words, 6+ letters, match phonics pattern\n\n"
"## Common Bugs\n"
"- Missing f-string prefix: {var} renders as literal text\n"
"- String concat in f-strings breaks evaluation\n"
"- Unicode in f-strings -> SyntaxError (use HTML entities)\n"
"- Word Swap: highlight ORIGINAL words, not swap targets\n"
"- Detective: exactly 8 pairs matching answers\n"
"- Writing lines in tables need position:relative"
),
metadata={"type": "architecture", "tool": "python"},
)
# 7. WEASYPRINT CONSTRAINTS
entities['weasyprint'] = e(
entity_id="curriculum_weasyprint_constraints",
category="curriculum/weasyprint",
entity_type="tool-constraint",
content=(
"# WeasyPrint PDF Constraints\n\n"
"## Supported\n"
"- Standard CSS, HTML tables, explicit page breaks\n"
"- border-collapse: separate for border-radius on cells\n"
"- Standard 14 PDF fonts built-in\n\n"
"## NOT Supported\n"
"- NO CSS floats, NO CSS Grid, NO inline-block wrapping\n"
"- NO ::before/::after pseudo-elements\n"
"- NO flex-wrap (single direction only)\n"
"- border-collapse: collapse breaks border-radius\n\n"
"## API\n"
"- from weasyprint import HTML\n"
"- HTML(string=html).write_pdf(path)\n\n"
"## Page Break Debugging\n"
"- break-after: avoid on .section-title / .section-note\n"
"- Explicit div with page-break-after: always\n\n"
"## Verification\n"
"- pdftotext output.pdf - | wc -l (text length)\n"
"- pdf2image (visual verification)\n"
"- PyMuPDF: len(doc.pages) for page count\n\n"
"## Related Skills\n"
"- weasyprint-page-break-debugging, weasyprint-pdf-page-breaks\n"
"- weasyprint-pdf-workflow, weasyprint-pagination"
),
metadata={"tool": "weasyprint", "type": "constraint-reference"},
)
# 8. 2ND GRADE PEDAGOGY (pinned)
entities['pedagogy'] = e(
entity_id="curriculum_pedagogy_2nd_grade",
category="curriculum/pedagogy",
entity_type="pedagogy",
pinned=True,
content=(
"# 2nd Grade Pedagogy Principles\n\n"
"## Learning Stage\n"
"- Age 7: Developing handwriting, building reading fluency\n"
"- Transitioning from learning-to-read to reading-to-learn\n"
"- Concrete operational thinking: needs visual/spatial representations\n"
"- Attention span: 15-20 minutes per activity\n\n"
"## Reading Development\n"
"- Decoding: Phonics patterns, vowel teams, digraphs, blends\n"
"- Encoding: Spelling rules, patterns, word structure\n"
"- Fluency: Sight words, reading speed, expression\n"
"- Comprehension: Q&A, drawing what they read\n"
"- Vocabulary: Context clues, synonyms, antonyms, word families\n\n"
"## Math Development\n"
"- Number Sense: Counting to 100, place value, comparing\n"
"- Operations: Add/sub within 100, intro to multiplication\n"
"- Measurement: inches/cm, time, money, weight\n"
"- Geometry: 2D/3D shapes, symmetry, area/perimeter\n"
"- Fractions: Intro to halves, thirds, fourths\n\n"
"## Writing Development\n"
"- Sky/fence/ground lines for letter formation\n"
"- Copy -> guided -> independent writing progression\n"
"- Sentence structure: capitals, punctuation, spacing\n"
"- Creative: stories, diaries, journals\n\n"
"## Assessment\n"
"- Friday assessments: review-focused, not punitive\n"
"- Celebration sections for motivation\n"
"- Checkpoints: Weeks 6, 20, 26, 32\n"
"- 12 words/week, 6+ letters minimum\n\n"
"## Activity Design\n"
"- 6-8 activities/page, mix of formats\n"
"- Visual: shapes, clocks, fraction circles, number lines\n"
"- Science: experiments, observation journals, engineering"
),
metadata={"grade": "2nd", "age": 7, "type": "pedagogy"},
)
# 9. HANDWRITING PRACTICE
entities['handwriting'] = e(
entity_id="curriculum_handwriting",
category="curriculum/handwriting",
entity_type="curriculum",
strength="moderate",
content=(
"# Handwriting Practice\n\n"
"## Specs\n"
"- Script: ~/.hermes/scripts/build_handwriting.py\n"
"- Output: ~/.hermes/output/handwriting_practice.pdf\n"
"- Page: 8.5x11\" (612x792 points), 22 line sets\n\n"
"## Line System\n"
"- Blue (RGB 0,0,1): 0.6pt solid - ground + sky\n"
"- Red (RGB 1,0,0): 0.4pt dashed [5 5] - fence midline\n"
"- Spacing: 0.2\" (14.4pt) between sets\n"
"- No top/boundary lines\n\n"
"## Letters\n"
"- Sky (touch top): b, d, f, h, k, l, t\n"
"- Fence (touch mid): a, c, e, g, i, m, n, o, r, s, u, v, w, x, z\n\n"
"## CRITICAL\n"
"- PyMuPDF Shape API corrupts dash patterns\n"
"- Must write raw PDF syntax for red dashes\n"
"- Standard 14 PDF fonts: Helvetica (F1), Helvetica-Bold (F2)\n\n"
"## Skills\n"
"- handwriting-practice-specs, build-handwriting-practice-pdf\n"
"- handwriting-practice-generator"
),
metadata={"type": "handwriting", "tool": "raw_pdf"},
)
# 10. AUDIT & QUALITY
entities['audit'] = e(
entity_id="curriculum_audit_quality",
category="curriculum/audit",
entity_type="workflow",
content=(
"# Curriculum Audit & Quality Assurance\n\n"
"## Pre-Generation\n"
"1. lint_week.py - catch f-string interpolation bugs\n"
"2. Verify word list: 12 words, 6+ letters, match pattern\n"
"3. Check previous week PDFs for layout reference\n"
"4. Delete stale PDFs before regenerating\n\n"
"## Post-Generation\n"
"1. Page counts: Mon=4, Tue=3, Wed=4, Thu=4, Fri=5\n"
"2. No content bleeding across page boundaries\n"
"3. All 12 words on Monday Word Cards and in story\n"
"4. Spelling test: 12 blank lines\n"
"5. No learning/remember boxes on Friday\n"
"6. Writing lines render in table cells\n\n"
"## Math Audits\n"
"- scripts/audit_math.py: skip-counting, place value, ordering, operators\n"
"- Activity count: 6 target, warn if <4 or >6\n\n"
"## ELA Audits\n"
"- Phonetic audit: each word matches stated pattern\n"
"- Detective: exactly 8 pairs matching answers\n"
"- Opposites, synonyms, antonyms consistency\n"
"- Context clues use words from current week\n\n"
"## Common Issues\n"
"- WeasyPrint caches stale PDFs -> always delete first\n"
"- f-string concat bugs -> literal text rendering\n"
"- Unicode in f-strings -> SyntaxError\n"
"- Page overflow -> reduce activities or add spacers\n"
"- border-radius on cells -> need border-collapse: separate"
),
metadata={"type": "quality-assurance"},
)
# 11. SPELLING WORKFLOW
entities['spelling'] = e(
entity_id="curriculum_spelling_workflow",
category="curriculum/spelling",
entity_type="workflow",
strength="moderate",
content=(
"# Spelling Curriculum Generator Workflow\n\n"
"## Location\n"
"~/Home_School/2nd_Grade/English_Language_Arts/Week_NN/generate_weekN.py\n\n"
"## Output\n"
"- 5 PDFs/week (Mon-Fri) via WeasyPrint\n\n"
"## Day Structure\n"
"- Mon (4p): Learning Box + Word Cards -> Story + Grammar -> Vowel Sort -> Copy/Fill/Opposites\n"
"- Tue (3p): Learning + Remember -> Scramble + Reading + Comp -> Swap + Builder\n"
"- Wed (3p): Learning + Reading -> Vocab + Syn/Ant + Word Web + Sort -> Fill Story + Context\n"
"- Thu (4p): Learning + Remember -> Spelling Story + Comp -> Detective + Swap -> Map + Paragraph\n"
"- Fri (3p): NO learning boxes -> Test + Review -> Builder + Journal + Celebration\n\n"
"## Rules\n"
"- 12 words/week, 6+ letters, match phonics pattern\n"
"- Friday: assessment only (no learning/remember boxes)\n"
"- Spelling test: 12 blank lines\n\n"
"## Building\n"
"- execute_code for incremental builds (header -> validate -> append days)\n"
"- NO patch on generators (fuzzy-match with HTML closing tags)\n"
"- Pre-evaluate dynamic content before f-strings"
),
metadata={"type": "spelling-generator"},
)
# ============================================================
# CREATE EDGES
# ============================================================
overview = entities['overview']
# part_of relationships
for key in ['math', 'ela', 'science', 'design', 'generators', 'weasyprint', 'pedagogy', 'handwriting', 'audit']:
add_edge(entities[key], 'part_of', overview)
# spelling is part_of ela
add_edge(entities['spelling'], 'part_of', entities['ela'])
# co_occurs relationships
add_edge(entities['math'], 'co_occurs', entities['ela'])
add_edge(entities['math'], 'co_occurs', entities['science'])
add_edge(entities['ela'], 'co_occurs', entities['science'])
add_edge(entities['design'], 'co_occurs', entities['weasyprint'])
add_edge(entities['generators'], 'co_occurs', entities['audit'])
add_edge(entities['pedagogy'], 'co_occurs', entities['handwriting'])
# Print summary
print(f"Curriculum knowledge ingestion complete!")
print(f"Entities created: {len(entities)}")
for key, eid in entities.items():
print(f" {key}: {eid}")
# Verify in DB
import sqlite3
db = os.path.expanduser('~/.hermes/brain/index/events.db')
conn = sqlite3.connect(db)
c = conn.cursor()
c.execute("SELECT COUNT(*) FROM entities WHERE deleted = 0")
total = c.fetchone()[0]
c.execute("SELECT COUNT(*) FROM entities WHERE deleted = 0 AND category LIKE 'curriculum%'")
curriculum = c.fetchone()[0]
print(f"\nDB total entities: {total}")
print(f"Curriculum entities: {curriculum}")
conn.close()
if __name__ == '__main__':
ingest()