Context
Scope42 currently ships as a React PWA reading a bespoke YAML file format. Since the project started, the way software architecture work happens has shifted toward AI coding agents operating directly on repository files. A dedicated web UI is no longer where the value lives.
This epic pivots scope42 into a small, opinionated convention plus tooling rather than an application:
- The only thing scope42 prescribes is a minimal, typed YAML frontmatter on each item file, plus a workspace config that tells tools where items live.
- The file format itself is open. Markdown (
.md), AsciiDoc (.adoc), or anything else that carries YAML frontmatter works. Tools never assume an extension.
- The directory structure is open. Users map item types to paths in the workspace config; there is no hard-coded
issues/ or decisions/.
- A standalone CLI linter validates frontmatter and typed relations.
- A recommended stack — including an Obsidian plugin — exists for users who want a ready-made UI, but is strictly optional.
- Agent skills teach coding agents how to read and produce scope42 items in any workspace.
- The existing React web app is deleted.
@scope42/data is kept as the shared schema/parsing library consumed by the linter, the plugin, and the skills.
For motivation also see #84
Format decisions
Frontmatter (scope42's only hard requirement)
Structured, minimal, typed. Parsed from YAML frontmatter at the top of any text file.
- Common to all types:
status, tags, created, modified, ticket
- Typed relation arrays (frontmatter, authoritative for linter & graph):
causedBy, resolves, modifies, creates, supersededBy, assesses
type is derived from the workspace config (which path the file lives in), not stored in frontmatter
title lives as the document's first heading (H1 in Markdown, = title in AsciiDoc), not in frontmatter
- All previously-structured prose fields (
description, context, drivers, options with pros/cons, outcome, deciders, comments) become free body content — scope42 stops modeling them
- Relation wikilinks (
[[issue-1]]) in body are allowed but not validated; they are a soft-reference mechanism surfaced by tools like Obsidian
Workspace config (new)
A single scope42.yaml at the workspace root tells all tools what's where.
items:
issue:
path: docs/issues
idPattern: "issue-*"
risk:
path: docs/risks
improvement:
path: docs/improvements
decision:
path: docs/adrs
extensions: [md, adoc]
- Any file in a configured path whose extension is in
extensions and that carries YAML frontmatter is treated as a scope42 item of that type
- No config = no assumptions; tools refuse to operate without it (explicit over implicit)
Sub-issues
Development happens on main. A release/v1 branch is only created on demand if a v1 patch needs to be shipped from the pre-pivot code.
Verification
npm run build passes for @scope42/data, @scope42/lint, @scope42/obsidian
- Migration CLI run against every
examples/ workspace produces files + a scope42.yaml that the new parser and linter accept
npx @scope42/lint examples/<workspace> exits 0 for every migrated example
- Linter correctly rejects fixtures for: missing required frontmatter field, unknown status, dangling relation ID, wrong-type relation target, filename not matching
idPattern, missing H1
- Linter accepts both a
.md fixture and a .adoc fixture with identical frontmatter — proves format agnosticism
- In Obsidian (with a migrated workspace + plugin loaded): all configured types appear in the sidebar; "New issue" command creates a file the linter accepts; native graph view shows relations
- An agent session invoking
scope42-making-decision against a blank workspace with a scope42.yaml produces a file passing the linter
- Root
npm run verify passes with app/ removed
Context
Scope42 currently ships as a React PWA reading a bespoke YAML file format. Since the project started, the way software architecture work happens has shifted toward AI coding agents operating directly on repository files. A dedicated web UI is no longer where the value lives.
This epic pivots scope42 into a small, opinionated convention plus tooling rather than an application:
.md), AsciiDoc (.adoc), or anything else that carries YAML frontmatter works. Tools never assume an extension.issues/ordecisions/.@scope42/datais kept as the shared schema/parsing library consumed by the linter, the plugin, and the skills.For motivation also see #84
Format decisions
Frontmatter (scope42's only hard requirement)
Structured, minimal, typed. Parsed from YAML frontmatter at the top of any text file.
status,tags,created,modified,ticketcausedBy,resolves,modifies,creates,supersededBy,assessestypeis derived from the workspace config (which path the file lives in), not stored in frontmattertitlelives as the document's first heading (H1 in Markdown,=title in AsciiDoc), not in frontmatterdescription,context,drivers,optionswith pros/cons,outcome,deciders,comments) become free body content — scope42 stops modeling them[[issue-1]]) in body are allowed but not validated; they are a soft-reference mechanism surfaced by tools like ObsidianWorkspace config (new)
A single
scope42.yamlat the workspace root tells all tools what's where.extensionsand that carries YAML frontmatter is treated as a scope42 item of that typeSub-issues
Development happens on
main. Arelease/v1branch is only created on demand if a v1 patch needs to be shipped from the pre-pivot code.@scope42/datafor Markdown frontmatter + workspace config@scope42/lintCLI for frontmatter & relation validationVerification
npm run buildpasses for@scope42/data,@scope42/lint,@scope42/obsidianexamples/workspace produces files + ascope42.yamlthat the new parser and linter acceptnpx @scope42/lint examples/<workspace>exits 0 for every migrated exampleidPattern, missing H1.mdfixture and a.adocfixture with identical frontmatter — proves format agnosticismscope42-making-decisionagainst a blank workspace with ascope42.yamlproduces a file passing the linternpm run verifypasses withapp/removed