feat: autonomous banner triage agent with 5-layer discovery cascade#343
Open
adme-dev wants to merge 418 commits intocloudflare:mainfrom
Open
feat: autonomous banner triage agent with 5-layer discovery cascade#343adme-dev wants to merge 418 commits intocloudflare:mainfrom
adme-dev wants to merge 418 commits intocloudflare:mainfrom
Conversation
New enrich-mitsubishi-colors.mjs walks the AEM DAM folder hierarchy
at /api/assets/mmal/vehicles/{model}/{MY}/.../{colour}/ to find
per-color CGI renders (3/4 front, side, rear-left).
158/172 colors now have hero images + 3-angle galleries (was 0%).
Covers Outlander, Triton, Pajero Sport with 14 unique paint renders.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
extractColors() now derives color names from 3 fallback sources when a code isn't in COLOR_MAP: 1. Swatch img alt text (e.g. alt="Honeycomb") 2. Swatch filename (e.g. excolorchip/honeycomb-DY2.gif) 3. Hero filename (e.g. colours-gt-line-honeycomb.webp) Added DY2 Honeycomb to COLOR_MAP. This ensures new color codes from BYO pages are named correctly without manual COLOR_MAP updates. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When sections are saved via the page builder, the page JSON is now flagged with manually_edited=true. The Brand Ambassador's shouldRegeneratePage() checks this flag and skips regeneration, preserving all composition edits. Also changed homepage crawl from every 2h to daily at 4am AEST. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ssador docs Updated 8 files: - Homepage crawl: every 2h → daily 4am AEST - Offers crawl: every 4h → daily 5am AEST - Added all-OEM data sync (daily 3am) to schedule tables - Brand Ambassador docs: added Manual Edit Protection section explaining manually_edited flag and page builder preservation - Updated skills/oem-crawl, workspace-crawler, crawl-config, OEM architecture docs with correct schedules Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Schedule defaults: homepage/offers now daily (1440min), was 2h/4h - Framework auto-detect: added gatsby, aem, magento, wordpress, sitecore - API data types: added colors, accessories, brochures options - Step 7: replaced stale cron setup with "What Happens Automatically" section showing daily sync, crawl, and Brand Ambassador pipelines - Deploy checklist: simplified, removed manual cron config (automatic) - Note about page builder edit protection in Brand Ambassador info Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Chery Motor Australia (chery-au) with Drupal CMS, 13 vehicle models: - Tiggo range: 4, 4 Hybrid, 7, 7 Super Hybrid, 8 Pro Max, 8 SH, 9 SH - Omoda sub-brand: 5, 5 GT, E5, E5 RYI - Chery: C5, E5 18 source pages, brand color #EB5757, no browser rendering needed. 71 dealer locations AU-wide. Pricing from $23,990 driveaway. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Initial data population for Chery Australia: - 22 products (13 models × Urban/Ultimate variants) - 85 variant_colors with hero renders + swatches from data-json attrs - 22 variant_pricing rows (driveaway, all 8 states) - 6 offers from /buying/offers page - 100% specs_json and key_features coverage Omoda sub-brand pages (5/5GT/E5/E5RYI) use different HTML structure, colors need separate extraction. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
31 files updated across docs, skills, workspaces, AGENTS.md, BRIEFING.md, cron config, registry, dashboard pages. Added chery-au to OEM ID lists and OpenClaw cron oem_ids array. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ng sync New seed-chery-accessories.mjs extracts accessories from per-model Drupal pages with prices, categories, and images. 211 accessories across 9 models (Tiggo 4/4H/7/7SH/8PM/8SH/9SH, C5, E5). Added chery-au to generic pricing sync in all-oem-sync.ts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- DEALER_API.md: added chery-au to available OEM IDs - use-page-builder.ts: added chery-au to OEM_IDS array - OEM_ONBOARDING.md: added Chery to history table - OEM_DATA_PIPELINES.md: added Chery row + updated totals - oem-data-sync SKILL.md: added 3 Chery seed scripts to registry Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…anners Comprehensive Volkswagen AU data from the OneHub Offers API: - 49 products across 15 models (was 6 products, 9 models) - 410 variant_colors with 4-angle renders + swatch tiles (was 6) - 49 variant_pricing rows with MRDP driveaway (was 5) - 41 offers with hero images (was 10) - 15 homepage banners per model family (was 3) - 11 brochure PDFs linked to vehicle_models - Polo, Golf, Tiguan, Touareg, Amarok, ID.4/5/Buzz, Tayron, T-Cross, T-Roc, Multivan, Caddy, Crafter all populated API: get-onehub-offers with version auto-discovery (starts at 547). Returns colors, pricing, features, brochures, banners per variant. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Hero section edits in the page builder were not propagating to the dealer website because header.slides (read by the site) and content.sections (written by the builder) were disconnected. - Save direction: update-sections now syncs hero desktop/mobile images, heading, sub_heading, and CTA back to header.slides[0] - Load direction: page builder seeds empty hero section fields from header.slides on load - Regenerate section: include full section schema in prompt and pass existing section data as context (fixes 500 validation errors) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…2 offers Full platform audit post VW/Chery onboarding: - 18 OEMs, 179 models, 796 products, 4952 colors, 1158 pricing rows - 302 offers, 176 banners, 2913 accessories, 231 source pages - Fixed: VW 15 offers added, Mitsubishi 3 banners, Subaru 3 banners - Chery Tiggo 7 spec sheet PDF linked as brochure Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- oem-data-sync SKILL.md: added seed-vw-onehub.mjs (products, colors, pricing, offers, banners, brochures) + VW OneHub notes - OEM_DATA_PIPELINES.md: added VW row with OneHub API details, updated totals to 796 products, 4952 colors, 302 offers, 176 banners - seed-discovered-apis.mjs: updated VW OneHub API schema with full return fields (4-angle renders, swatch tiles, brochures, features) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Click any offer card to open a dialog showing: - Full hero image - Pricing breakdown (driveaway, ABN, savings, raw price string) - Validity dates with expired badge - Eligibility criteria - CTA + source links - Collapsible disclaimer (renders HTML when available, falls back to text) - Metadata (OEM, ID, last seen) Enriched VW offers with full OneHub data: finance rates, per-week pricing, HTML disclaimers, banner validity, eligibility. Swept all 18 OEMs to fill missing descriptions, disclaimers, and images. 302/302 offers with images (100%), 299 with descriptions, 300 with disclaimers. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Added universal_disclaimer to each OEM's config_json:
"Offers shown are sourced from OEM websites and may not reflect the
most current terms. Prices are manufacturer's recommended driveaway
prices and may vary by dealer and location. Contact your local dealer
for the latest pricing, availability, and conditions."
Dealer API /catalog endpoint now returns { models, universal_disclaimer }
so dealer sites can render it as a legal footer.
Also enriched Toyota (bZ4X deposit bonus + JetCharge), Suzuki (full
legal fine print with purchase/delivery dates), and Subaru (retailer
terms) with real disclaimers extracted via browser automation.
302/302 offers with text disclaimers, 68 with HTML formatting.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Updated 11 files with accurate platform totals: - 18 OEMs, 179 models, 796 products, 4,952 colors, 1,158 pricing - 302 offers (100% images, 100% disclaimers), 176 banners - 2,913 accessories, 2,981 accessory-model joins, 106 brochures - 231 source pages BRIEFING.md final state updated to 2026-03-19 with full session summary including VW OneHub, Chery onboarding, universal disclaimers, offer detail dialog, and Brand Ambassador edit protection. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. VW OneHub sync added to all-oem-sync.ts — runs daily alongside Hyundai/Mazda/Mitsubishi. Upserts 49 products, 410 colors with 4-angle renders, pricing, and 15 offers from the API. 2. Auto-fallback offer images — fixOfferImages() runs after every sync, finds offers with no hero_image_r2_key and pulls the first hero image from matching variant_colors. Prevents blank offer cards. 3. Both run in parallel with existing syncs for zero added latency. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…weekly report
Phase 1 of enterprise OpenClaw integration:
Health Check (every 4h):
- Queries import_runs success rate per OEM (last 24h)
- Detects degraded OEMs (< 70% success rate)
- Detects missed OEMs (0 runs in 24h)
- Sends Slack alert with degraded/missed OEM details
Memory Sync (every 30min):
- Backs up OEMs config + discovered APIs to R2
- Timestamped backups: memory/backups/{date}/
- Auto-cleans backups older than 7 days
Weekly Report (Monday 9am):
- Aggregates 7-day crawl metrics (runs, success rate, changes)
- Current platform totals (products, colors, offers, banners)
- Sends formatted Slack digest with field layout
All three wire into existing Slack notification infrastructure
(SlackNotifier class was already built, just not called).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… management Phase 2 of enterprise OpenClaw integration: Orchestrator Controller (every 2h via OpenClaw cron): - Monitors all 18 OEMs health from import_runs (last 24h) - Classifies: healthy (>70%), degraded (30-70%), failing (<30%), stale (0 runs/36h) - Auto-retries failed OEMs with exponential backoff (30min → 12h max) - Escalates to Slack when max retries exceeded (5 consecutive failures) - Persists retry state in R2 (memory/controller/retry-state.json) - Rich Slack alerts with per-OEM status, retry counts, error messages Admin API: - GET /admin/system-status — on-demand health check returning full per-OEM breakdown (status, success rate, runs, last error, retry count) OpenClaw cron job: - oem-orchestrator skill, every 2h, configurable thresholds This is the "traffic controller" — self-aware (health monitoring), self-healing (auto-retry), and human-observable (Slack escalation). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New skill: skills/oem-orchestrator/SKILL.md — Traffic Controller documentation covering health monitoring, auto-retry with backoff, Slack escalation, and system status API. Updated: - BRIEFING.md: 15 skills (was 14), orchestrator in skills table, split cron schedule into Cloudflare + OpenClaw sections - workspace/AGENTS.md: added orchestrator + updated agent-hooks desc - docs/CRON_SYSTEM.md: updated all 6 OpenClaw job descriptions with current implementation details, added orchestrator as job cloudflare#6 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- vectorize-pdfs.mjs: fixed pdf-parse ESM import for Node 24 (createRequire workaround for CJS module) - oem-sales-rep SKILL.md: complete rewrite for OpenClaw native use. Now includes API endpoints, OEM IDs, platform totals, and example queries for products, offers, colors, accessories, and system health. Works via OpenClaw Slack channel without custom handler code. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- PDFParse v4 uses constructor(Uint8Array) + load() + getPageText() - Fixed Buffer → Uint8Array conversion - Fixed CJS/ESM interop via createRequire - Handles image-based PDFs gracefully (skips with warning) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replaced pdf-parse with Gemini 2.5 Flash native PDF understanding for vectorize-pdfs.mjs. Handles image-based brochures that pdf-parse can't read (all 108 OEM brochures are image-based marketing PDFs). - Validates %PDF header before processing (catches HTML redirects) - Falls back to pdf-parse for files >20MB (Gemini inline limit) - Extracts all text including specs, features, prices, disclaimers - Tested: Chery Tiggo 7 spec sheet → 9,211 chars extracted Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
text-embedding-004 was removed from v1beta API. Using gemini-embedding-001 with outputDimensionality: 768 to match the pdf_embeddings vector(768) column. Gemini 2.5 Flash vision extracts text from image-based PDFs, gemini-embedding-001 generates 768-dim embeddings per chunk. 401+ embeddings already populated across 5 OEMs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The sync_embeddings cron handler (every 6h) now: 1. Finds brochures in vehicle_models.brochure_url not yet in pdf_embeddings 2. Downloads PDF, validates %PDF header 3. Extracts text via Gemini 2.5 Flash vision (handles image-based PDFs) 4. Chunks into 800-char segments with 100-char overlap 5. Generates 768-dim embeddings via gemini-embedding-001 6. Upserts into pdf_embeddings with source_id dedup Max 10 brochures per run (Worker CPU limit). Remaining queued for next cycle. New brochures from onboarding or crawl updates are automatically picked up within 6 hours. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cloudflare cron fires in UTC. Adjusted all schedules: - 0 17 * * * = 4am AEDT (homepage crawl) - 0 18 * * * = 5am AEDT (offers crawl) - 0 6,18 * * * = 5am + 5pm AEDT (vehicles crawl, twice daily) - 0 19 * * * = 6am AEDT (news crawl) - 0 20 * * * = 7am AEDT (sitemap crawl) Also fixed OpenClaw OEM cron jobs — switched from anthropic/claude-sonnet-4-6 (not in allowed models) to groq/llama-3.3-70b-versatile. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Kia's homepage uses .main_wrap/.kv_txtArea/.big_title instead of .hero-carousel — old selectors matched nothing, so zero banners were captured. Also adds mobile image extraction from <picture> sources. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…icates Two fixes: 1. _generated_html now uses inline styles from computed values instead of Tailwind classes. Tailwind classes in v-html don't render because they're not in the compiled CSS. Inline styles work everywhere. 2. "HTML → Tailwind" menu option now adds to queue instead of emitting directly. Prevents duplicate sections (left-click queue + right-click direct emit). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
_generated_html sections are now contenteditable — click any text (title, subtitle, button, paragraph) to edit it directly. Changes save on blur back to the section's _generated_html field. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The inline formatting toolbar (font size, weight, alignment, color) now appears when focusing on a _generated_html contenteditable block. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Click any link/button in a _generated_html section to open a floating URL editor. Shows current href, editable input, Save button. Saves the updated href back to the section's _generated_html. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…arrays Empty arrays are truthy in JS, so `llmResult.banner_slides || cssData` chose the empty LLM array over valid CSS results. Now checks `.length` before preferring LLM data. Same fix applied to products and offers. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…n images 404
PageCapturer generated proxy paths as /media/pages/{oem}/{model}/{file} but the
media route only served /media/pages/assets/{oem}/{model}/{file}. Added a compat
route for existing stored pages and fixed the capturer to emit the canonical path.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- syncFoton() now scrapes Tunland colors (8×4 variants), Aumark S White (24 trucks), and brochure/spec sheet PDFs into cta_links - Fixed seed-foton-colors.mjs variant matching (V7 C 4x2 vs 4x4) - Updated docs, skills, sync-manifest, and cron-jobs description Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…assertion - Fetch Tunland + Aumark S pages once in syncFoton(), pass HTML to helpers - syncFotonBrochures now merges by type instead of overwriting cta_links - Replace non-null assertion with const binding after guard Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Last 3 OEMs in the 6am vehicles crawl (mazda, mitsubishi, toyota) were consistently stuck as "running" because CONCURRENCY=3 meant 6 batches × ~90s = ~9min, hitting the 10-min CF cron limit. CONCURRENCY=6 = 3 batches × ~90s = ~4.5min with plenty of headroom. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Critical: - SSRF: Added validateUrl() helper applied to both proxy-html AND capture-screenshot endpoints. Fixed 172.x blocking to only 172.16-31. - XSS: Strip on* event handlers and dangerous tags (iframe, object, embed, form) from captured HTML in tailwindHtml() Important: - Deduplicated image download pipeline into downloadImagesToR2() - Removed dead sectionSpacingStyle function - Added random suffix to screenshot R2 keys for collision protection Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…EM, window globals Implements Task 3 of the banner triage agent: extracts structured data embedded in OEM HTML pages via schema.org JSON-LD, framework-specific hydration payloads, AEM model JSON, and window.* state objects. 18 unit tests pass covering all synchronous extractors. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…scoring Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- LDV: update heroSlides from broken `.hero-carousel, [class*="hero"]` to `[class*="HhCarouselWrapper"] [class*="EmbCarouselItemWrapper"]` (i-motor Gatsby uses camelCase class names, CSS attr selectors are case-sensitive) - engine.ts: add img[alt] as headline fallback when no heading text found Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Design spec: 5-layer discovery cascade architecture - Implementation plan: 9 tasks with TDD - SKILL.md: OpenClaw agent skill definition - cron-jobs.json: banner-triage entry (event-driven) - AUTONOMOUS_AGENT_WORKFLOWS.md: Workflow cloudflare#9 - AGENTS.md: updated skill count and agent listing Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ery efficiency - Add isAllowedUrl() SSRF guard to banner-triage.ts; block non-HTTPS and private IP ranges in Layer 1 (discovered APIs), Layer 3 (page fetch), Layer 4 (page fetch) - Fix selector_type: 'hero_slides' → 'heroSlides' in Layer 4 upsert to match extractWithSelectors key and DB convention - Add getEffectiveSelectors() export to engine.ts for callers to wire DB overrides into the extraction pipeline via extractWithSelectors selectorOverrides param - Optimise crawl-doctor stale banner query: fetch only rows with last_seen_at < 72h ago instead of all banners then filtering in-memory - Log failed banner upserts instead of silently swallowing the error - Move JSONLD_RE with /gi flag inside extractJsonLdBanners() to prevent shared mutable lastIndex state across calls Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- BRIEFING.md: 7→8 agents, 16→17 cron jobs - OPENCLAW_OPTIMIZATION.md: 7→8 agents - workspace/AGENTS.md: 7→8 agents with banner-triage - OEM_AGENT_ARCHITECTURE.md: add banner self-healing, Crawl Doctor staleness, triage row Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…havior The implementation treats non-JSON AI responses as raw template HTML (last-resort fallback). Test was asserting the old pre-fallback behavior. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Enable requiresBrowserRendering for suzuki-au (hero images are JS-injected) - Update heroSlides selector to .hb-2025-refresh__item (actual carousel class) - Fix extractImageUrl to parse background-image from inline style attrs - Add extractBgImageUrl helper for mobile bg-image extraction - Rewrite seed-suzuki-banners.mjs to use Puppeteer scraping (no more hardcoded URLs) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
HEAD-requests every banner image URL, detects CDN 404s and broken links. Broken URLs are nulled out immediately (no dead images in dashboard). Emits banner_extraction_failed change event to trigger triage agent. Batched in groups of 20 with 5s timeout per request. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Flexible categorised specs with well-known keys, AI extraction via Gemini Flash from existing PDF chunks, dashboard page, REST API. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extracted from crawl doctor (every 2h) into standalone daily job. HEAD-checks all banner image URLs, nulls broken ones, emits banner_extraction_failed events for triage agent pickup. ~393 HEAD requests/day, $0.003/month CF cost. New CF cron trigger: 30 17 * * * (4:30am AEDT) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
img[alt]headline fallback for OEMs with no heading text in bannersArchitecture
When banner extraction returns 0 results for an OEM that previously had banners, the system emits a
banner_extraction_failedchange event. The banner-triage agent then runs a 5-layer cascade:Discovered selectors stored in
selector_overridestable (30-day TTL) so future crawls self-heal without redeployment.New files
supabase/migrations/20260402_selector_overrides.sqlsrc/extract/banner-data-filter.ts(+ 48 tests)src/extract/inline-data.ts(+ 18 tests)src/sync/banner-triage.tsskills/autonomous-agents/banner-triage/SKILL.mddocs/superpowers/specs/2026-04-02-banner-triage-agent-design.mdModified files
src/extract/engine.ts— selector overrides support +img[alt]fallbacksrc/orchestrator.ts— banner extraction failure detectionsrc/sync/crawl-doctor.ts— banner staleness checksrc/routes/cron.ts— banner-triage skill registrationsrc/oem/registry.ts— LDV selector fixTest plan
🤖 Generated with Claude Code