JSON/JSONL Standards¶
Purpose: Standards for JSON configuration files and JSONL data formats used in this repository.
Scope¶
This document covers: JSON formatting, JSONL conventions (beads, logs), schema validation, and tool configuration patterns.
Related: - YAML/Helm Standards - Alternative configuration format - Markdown Style Guide - Documentation conventions - Python Style Guide - Python JSON handling
Quick Reference¶
| Standard | Value | Validation |
|---|---|---|
| Indentation | 2 spaces | jq . or Prettier |
| Trailing Newline | Required | Editor config |
| Trailing Commas | Not allowed (JSON spec) | JSON parser |
| Comments | Not allowed (JSON spec) | Use JSONC for config |
| Line Length | No hard limit | Keep readable |
| JSONL Delimiter | Newline (\n) |
One object per line |
Format Decision Tree¶
Text Only
What is the file's purpose?
├─ Configuration (human-edited)
│ ├─ Needs comments? → Use JSONC (*.jsonc) or YAML
│ └─ No comments needed? → Use JSON with descriptive keys
├─ Data interchange
│ ├─ Single record/object → JSON
│ └─ Multiple records (append-only) → JSONL
├─ Log/event data → JSONL
└─ Schema definition → JSON Schema (*.schema.json)
JSON Formatting¶
Standard Format¶
JSON
{
"name": "example",
"version": "1.0.0",
"config": {
"timeout": 30,
"retries": 3,
"enabled": true
},
"items": [
"first",
"second",
"third"
]
}
Formatting Rules¶
| Rule | Example | Why |
|---|---|---|
| 2-space indent | "key": "value" |
Readability, smaller files |
| Double quotes only | "key" not 'key' |
JSON spec requirement |
| No trailing commas | ["a", "b"] not ["a", "b",] |
JSON spec requirement |
| Trailing newline | File ends with \n |
POSIX standard, git diffs |
| UTF-8 encoding | Always | Universal compatibility |
Key Naming¶
| Convention | Use For | Example |
|---|---|---|
camelCase |
JavaScript/TypeScript config | "apiVersion" |
snake_case |
Python config, beads | "issue_type" |
kebab-case |
Avoid (quoting issues) | - |
UPPER_CASE |
Environment variables only | "DATABASE_URL" |
ALWAYS: Be consistent within a file. Match the ecosystem convention.
JSONL Format¶
What is JSONL?¶
JSON Lines: one valid JSON object per line, newline-delimited.
Text Only
{"id": "abc-123", "status": "open", "title": "First issue"}
{"id": "abc-124", "status": "closed", "title": "Second issue"}
{"id": "abc-125", "status": "open", "title": "Third issue"}
When to Use JSONL¶
| Use JSONL | Use JSON |
|---|---|
| Append-only data (logs, events) | Single configuration object |
| Streaming ingestion | Nested hierarchical data |
| Line-by-line processing | Small datasets (<100 records) |
| Beads issues tracking | API responses |
| Large datasets | Human-edited files |
JSONL Rules¶
| Rule | Rationale |
|---|---|
| One object per line | Enables grep, head, tail |
| No trailing comma | Each line is complete JSON |
| No array wrapper | Not [{...}, {...}] |
| Newline after last record | POSIX, append-friendly |
| UTF-8, no BOM | Universal compatibility |
Processing JSONL¶
Bash
# Count records
wc -l issues.jsonl
# Filter by field
jq -c 'select(.status == "open")' issues.jsonl
# Extract field
jq -r '.title' issues.jsonl
# Pretty-print one record
head -1 issues.jsonl | jq .
# Append new record
echo '{"id": "new", "status": "open"}' >> issues.jsonl
Beads JSONL Format¶
The .beads/issues.jsonl file uses a specific schema:
Issue Record Schema¶
JSON
{
"id": "prefix-xxxx",
"title": "Issue title",
"status": "open",
"priority": 2,
"issue_type": "task",
"owner": "user@example.com",
"created_at": "2026-01-15T08:18:34.317984-05:00",
"created_by": "User Name",
"updated_at": "2026-01-15T08:42:39.253689-05:00",
"closed_at": null,
"close_reason": null,
"dependencies": []
}
Field Reference¶
| Field | Type | Required | Values |
|---|---|---|---|
id |
string | Yes | prefix-xxxx format |
title |
string | Yes | Brief description |
status |
string | Yes | open, in_progress, closed |
priority |
integer | Yes | 0-4 (0=critical, 4=backlog) |
issue_type |
string | Yes | task, bug, feature, epic, rig, agent |
owner |
string | No | Email address |
created_at |
string | Yes | ISO 8601 timestamp |
created_by |
string | Yes | Creator name |
updated_at |
string | Yes | ISO 8601 timestamp |
closed_at |
string | No | ISO 8601 timestamp or null |
close_reason |
string | No | Reason for closing |
dependencies |
array | No | Dependency objects |
Dependency Object¶
JSON
{
"issue_id": "prefix-child",
"depends_on_id": "prefix-parent",
"type": "parent-child",
"created_at": "2026-01-15T08:19:32.440350-05:00",
"created_by": "User Name"
}
Configuration Files¶
tsconfig.json¶
JSON
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"strict": true,
"outDir": "./dist"
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
package.json¶
JSON
{
"name": "package-name",
"version": "1.0.0",
"description": "Brief description",
"main": "dist/index.js",
"scripts": {
"build": "tsc",
"test": "jest",
"lint": "eslint ."
},
"dependencies": {
"dependency": "^1.0.0"
},
"devDependencies": {
"typescript": "^5.0.0"
}
}
VS Code settings.json¶
JSON
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"files.insertFinalNewline": true,
"files.trimTrailingWhitespace": true
}
JSON Schema¶
Defining Schemas¶
JSON
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/config.schema.json",
"title": "Configuration",
"type": "object",
"required": ["name", "version"],
"properties": {
"name": {
"type": "string",
"description": "Project name",
"minLength": 1
},
"version": {
"type": "string",
"pattern": "^\\d+\\.\\d+\\.\\d+$"
},
"enabled": {
"type": "boolean",
"default": true
}
},
"additionalProperties": false
}
Schema Validation¶
Bash
# Using ajv-cli
npx ajv validate -s schema.json -d config.json
# Using Python jsonschema
python -c "
import json
from jsonschema import validate
with open('schema.json') as s, open('config.json') as c:
validate(json.load(c), json.load(s))
"
Common Errors¶
| Symptom | Cause | Fix |
|---|---|---|
Unexpected token |
Trailing comma | Remove comma after last item |
Unexpected token ' |
Single quotes | Use double quotes only |
Unexpected token / |
Comments in JSON | Use JSONC or remove comments |
Invalid character |
BOM or wrong encoding | Save as UTF-8 without BOM |
Unexpected end of input |
Truncated file | Check for complete structure |
| JSONL parse error | Multi-line object | Ensure one object per line |
| Git merge conflict in JSONL | Concurrent edits | Append-only, resolve manually |
Anti-Patterns¶
| Name | Pattern | Why Bad | Instead |
|---|---|---|---|
| Minified Config | {"a":1,"b":2} |
Unreadable, hard to diff | Pretty-print with 2 spaces |
| Comments in JSON | // comment |
Invalid JSON, breaks parsers | Use JSONC or key naming |
| Mixed Conventions | camelCase + snake_case |
Inconsistent, confusing | Pick one per file |
| Nested Arrays of Arrays | [[[]]] |
Hard to parse, validate | Flatten or use objects |
| Magic Numbers | "priority": 2 |
Meaning unclear | Document or use enums |
| No Schema | Large config without schema | No validation, drift | Add JSON Schema |
Tooling¶
Formatting¶
Bash
# jq - Format and validate
jq . config.json > formatted.json
cat config.json | jq .
# Prettier - Format with config
npx prettier --write '**/*.json'
# Python - Format
python -m json.tool config.json
Validation¶
Bash
# jq - Check valid JSON
jq empty config.json && echo "Valid" || echo "Invalid"
# Python - Check valid JSON
python -c "import json; json.load(open('config.json'))"
# Node - Check valid JSON
node -e "require('./config.json')"
JSONL Processing¶
Bash
# Validate each line
while read -r line; do
echo "$line" | jq empty || echo "Invalid line: $line"
done < data.jsonl
# Convert JSON array to JSONL
jq -c '.[]' array.json > data.jsonl
# Convert JSONL to JSON array
jq -s '.' data.jsonl > array.json
Editor Configuration¶
.editorconfig¶
INI
[*.json]
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8
[*.jsonl]
indent_style = space
indent_size = 0
insert_final_newline = true
.prettierrc¶
JSON
{
"tabWidth": 2,
"useTabs": false,
"trailingComma": "none",
"singleQuote": false
}
Summary¶
Key Takeaways:
- 2-space indentation, trailing newline, UTF-8 encoding
- No trailing commas (JSON spec)
- Use JSONL for append-only data, JSON for config
- Match key naming to ecosystem (camelCase for JS, snake_case for Python)
- Add JSON Schema for large/shared config files
- Process JSONL with
jq -cfor streaming - Validate JSON with
jq emptyor language parser - Beads uses JSONL with specific issue schema
- Use Prettier or jq for consistent formatting