This file provides guidance to agents when working with code in this repository.
The information here is generated with Claude's /init command. It smells right but is not fully vetted, so caveat lector.
Positron is Artsy's editorial CMS (internally called "Writer"). It's a full-stack Node.js application combining an Express API with a client-side editorial interface. The application manages articles, authors, channels, curations, and other editorial content that gets distributed to Artsy.net, Google AMP, RSS feeds, and Google News.
# Start the development server (runs on port 3005 with hot-reload)
yarn start
# Start with debugging enabled (port 3005 with inspect mode)
yarn dev# Run all tests (mocha + jest)
yarn test
# Run only jest tests
yarn jest
# Run jest tests in watch mode
yarn test:watch
# Run only mocha tests
yarn mocha <path>
# Run single test file
yarn mocha src/api/apps/articles/test/model.test.coffee
yarn jest src/client/components/article_list/test/ArticleList.test.tsx# Run linter
yarn lint
# Run linter with auto-fix
yarn lint --fix
# Run type checking
yarn type-check
# Format code with Prettier
yarn prettier-project# Start with Hokusai Dev (includes MongoDB, OpenSearch, and Positron)
COMMIT_HASH=$(git rev-parse --short HEAD) hokusai dev start
# Run tests with Hokusai
hokusai testPositron is organized as two separate Express applications that are combined at boot time:
- API (
src/api/): Private JSON REST API + GraphQL endpoint - Client (
src/client/): Server-rendered editorial interface (the "Writer" app)
Both apps are mounted in src/boot.js, which is loaded by src/index.js.
The API follows a Transaction Script pattern rather than traditional OOP models:
- Apps (
src/api/apps/): Sub-Express apps representing resources (articles, authors, channels, etc.)- Each app contains:
index.coffeeorindex.js: App setup and middlewareroutes.coffeeorroutes.js: Route handlers (like Rails controllers)model/: Database transaction functions (NOT ActiveRecord-style objects)index.js: Main model logicschema.coffee: Joi validation schemasretrieve.js: Query building and retrievalsave.coffee: Persistence logic- Other domain-specific modules as needed
- Each app contains:
- Models as Libraries: Models are collections of functions that operate on plain data objects using MongoDB (via mongojs), not ORM instances
- Validation: Uses Joi for schema validation (
src/api/lib/joi.coffee) - Authentication: All endpoints require Artsy
X-Access-Tokenheader; uses Gravity for auth - Special param:
mecan be used in place of user IDs (e.g.,/articles?author_id=me)
The client is a server-rendered application using a mix of React, Backbone, and legacy code:
- Apps (
src/client/apps/): Feature-specific sub-applicationsedit/: Article editing interfacearticles_list/: Browse/manage articlessettings/: Configuration and adminqueue/: Article publishing queue- Each app has its own routes, components, and client-side entry point
- Components (
src/client/components/): Reusable UI components (mix of React and Backbone) - Mix of Technologies:
- React (modern components)
- Backbone (legacy components)
- Draft.js (rich text editing)
- Redux (state management in some areas)
- Styled Components (styling)
Channels: Groups of users with specific permissions. Articles belong to channels, which determine available layouts and features. Essential for multi-team editorial management.
Article Layouts: Different visual presentations (standard, feature, series, news, video, etc.). Layout determines available editing features in Writer.
Custom Editorial Features: Special one-off articles with unique layouts/experiences. Implemented via EditorialFeature component in the Reaction library (see doc/editorial-features.md).
SuperArticles (legacy): Articles with is_super_article: true. Can have related articles and sponsor fields. Being deprecated in favor of more flexible approach.
Curations: Schema-less content model for highly custom content that doesn't fit article structure. Requires custom edit UI and rendering.
When articles are published, content is distributed to:
- Artsy.net (via Metaphysics and Force)
- Google AMP (
/ampendpoint) - RSS feeds
- Google News (via sitemaps)
See doc/distribution.md for details.
Positron exposes a GraphQL endpoint at /api/graphql:
- Built with joiql (Joi + GraphQL)
- Queries for articles, authors, channels, curations, tags
- GraphiQL interface available in development
- See
src/api/apps/graphql/index.js
- MongoDB: Main database (accessed via
mongojswrapper) - OpenSearch/Elasticsearch: Search functionality (see
src/api/apps/search/) - Connection configured via
MONGOHQ_URLenvironment variable - No ORM - uses direct MongoDB queries with vanilla JS objects
The codebase contains multiple languages and should be maintained consistently:
- CoffeeScript: Legacy code, primarily in API routes and older models
- JavaScript (ES6+): Newer API code and utilities
- TypeScript: React components and modern client code
- Stylus: Styling (legacy)
- Styled Components: Modern component styling
When editing existing files, maintain the file's current language. For new files, prefer TypeScript/JavaScript over CoffeeScript.
Tests use two patterns:
*.test.coffeeor*.test.js/*.test.tsx: Mocha tests (legacy)*.spec.ts/*.spec.tsx: Jest tests (modern)
- Copy
.env.exampleto.env - Key environment variables:
MONGOHQ_URL: MongoDB connection stringARTSY_URL,ARTSY_ID,ARTSY_SECRET: Artsy API credentialsNODE_ENV: development/staging/productionPORT: Default 3005
- For Artsy devs: Can point to staging database (requires VPN)
- For local MongoDB: Must create
channelscollection and add user
- Base path:
./src(allows absolute imports from src) - JSX: React
- Strict null checks enabled
- Module resolution: node
- Type checking:
yarn type-check
- CI: CircleCI
- Staging: Auto-deploys from
mainbranch - Production: Deploy by creating PR from
stagingtoreleasebranch - Kubernetes deployment (see README for links)
- Docker-based via Artsy's Hokusai tool
- Force: Artsy.net frontend - renders published articles
- Reaction: Component library - contains article display components
- Metaphysics: GraphQL gateway - may query Positron data
- Gravity: Artsy's main API - handles authentication
Server-side debugging:
- Run
yarn dev(starts with--inspect) - Navigate to
chrome://inspect - Click "inspect" under Remote Target
- Use
debuggerstatements in code
- MongoDB must be running locally for tests (unless using Hokusai)
- Changes to server code in development mode are hot-reloaded via
@artsy/express-reloadable - Webpack dev middleware provides client-side hot reloading
- Socket.io used for real-time features (article locking, etc.)
meis a reserved keyword - don't use as an ID- Article distribution (to Force, AMP, etc.) happens on publish, not on save