Add detailed documentation for Non-Linear project, including vision, data model, views and UI, policy model, agent integration, and scope/roadmap. Replace previous markdown files with updated versions for clarity and consistency.

This commit is contained in:
Cнесарев Максим 2026-05-05 16:24:55 +03:00
parent de0265f1b5
commit 610b9a26e2
13 changed files with 961 additions and 544 deletions

View File

@ -2,7 +2,7 @@
## One-liner ## One-liner
A graph-native issue tracker for small IT teams where both humans and AI agents navigate a shared decomposition tree of project structure. A graph-native issue tracker for small IT teams where both humans and AI agents navigate a shared decomposition tree of components and issues, with first-class multi-repo integration.
## Core Thesis ## Core Thesis
@ -10,11 +10,13 @@ Software projects are easier to conceptualize top-down using graphs. Traditional
## Why Now ## Why Now
1. **AI agents need topology.** Current tools bolt on AI after the fact. An agent that wants to understand "what should I work on next?" or "what's blocked?" has to reverse-engineer structure from flat ticket lists. A graph-native model gives agents *rails to traverse*. This is a structural advantage, not a feature. 1. **AI agents need topology.** Current tools bolt on AI after the fact. An agent that wants to understand "what should I work on next?" or "what's blocked?" has to reverse-engineer structure from flat ticket lists. A graph-native model gives agents *rails to traverse*.
2. **Small teams are underserved.** There's a gap between "too simple" (Trello, GitHub Issues) and "too heavy" (Jira, Azure DevOps). Small teams need more structure than a Kanban board but can't justify Jira administration overhead. 2. **Small teams are underserved.** There's a gap between "too simple" (Trello, GitHub Issues) and "too heavy" (Jira, Azure DevOps). Small teams need more structure than a Kanban board but can't justify Jira administration overhead.
3. **Agent ecosystems are emerging.** Building for AI-agent workflows is a bet on timing. Teams are beginning to use agents for code review, task decomposition, triage, and status reporting. An agent-native tracker is positioned for this shift. 3. **Agent ecosystems are emerging.** Teams are beginning to use agents for code review, task decomposition, triage, and status reporting. An agent-native tracker is positioned for this shift.
4. **Code structure is the natural skeleton.** Most projects already have a structure — it's in their repos. Connecting code and inferring the project skeleton eliminates the cold-start problem that kills adoption of structured tools.
## Target Users ## Target Users
@ -26,19 +28,28 @@ Software projects are easier to conceptualize top-down using graphs. Traditional
| Tool | Strength | Gap | | Tool | Strength | Gap |
|------|----------|-----| |------|----------|-----|
| Linear | Fast, opinionated, clean | Flat structure, AI retrofitted | | Linear | Fast, opinionated, clean | Flat structure, AI retrofitted, no code-aware skeleton |
| Jira | Powerful, extensible | Heavy, complex, AI bolted on | | Jira | Powerful, extensible | Heavy, complex, AI bolted on |
| GitHub Issues/Projects | Integrated with code | Minimal structure | | GitHub Issues/Projects | Integrated with code | Minimal structure, single-repo |
| Plane.so | Open-source Linear alternative | Same flat model | | Plane.so | Open-source Linear alternative | Same flat model |
| Trello | Simple, visual | No hierarchy, no agent support | | Trello | Simple, visual | No hierarchy, no agent support |
| Kumu / Obsidian Canvas | Graph modeling | Not issue trackers | | Kumu / Obsidian Canvas | Graph modeling | Not issue trackers |
**The gap:** No tool combines graph-native project modeling with AI-agent-first API design. **The gap:** No tool combines graph-native project modeling, multi-repo code integration, and AI-agent-first API design.
## Design Principles ## Design Principles
1. **Graph is the spine.** The decomposition tree defines project structure. Everything else — views, permissions, agent navigation — derives from graph position. 1. **Graph is the spine.** The decomposition tree defines project structure. Everything else — views, permissions, agent navigation — derives from graph position.
2. **Depth is type.** Issues are untyped. A node's position in the tree implies its abstraction level. Root = project, children = components, leaves = tasks. Labels handle orthogonal concerns. 2. **Two node types.** Components are the skeleton (stable, map to code). Issues are the work (flow through statuses, get assigned). Cleaner than untyped depth-as-type.
3. **Two graphs, separated.** The decomposition tree (strict parent→child hierarchy) and the association graph (lateral links like "blocks", "relates-to") are distinct relationship types. The tree is structural; links are annotation. 3. **Two graphs, separated.** The decomposition tree (strict parent→child) and the association graph (lateral links) are distinct. The tree is structural; links are annotation.
4. **Agents are first-class actors.** Not assistants bolted on — agents have accounts, roles, permissions, and can traverse the graph independently. 4. **Code is the skeleton.** Connect your repos, infer the component tree. The fastest path from "nothing" to "structured project" is through code you already have.
5. **Granular trust.** The permission system is policy-based from day one. Roles are convenience bundles over a granular engine, not hardcoded ceilings. 5. **Agents are first-class actors.** Not assistants bolted on — agents have accounts, roles, permissions, and can traverse the graph independently.
6. **Granular trust.** The permission system is policy-based from day one. Roles are convenience bundles over a granular engine, not hardcoded ceilings.
7. **Keyboard-first.** Every action has a shortcut. The command palette is the primary navigation method.
8. **Plan-then-apply.** Structural changes show a preview of consequences before committing.
## Three Fast-Start Paths
1. **Connect your code:** OAuth to GitHub/GitLab → select repos → AI infers component skeleton → adjust → start adding issues
2. **Clean start:** Create project → root node → add components and issues manually
3. **Import from tracker (v0.2+):** Import from Linear/Jira/GitHub Issues → infer hierarchy → adjust

206
02-DATA-MODEL.md Normal file
View File

@ -0,0 +1,206 @@
# Non-Linear: Data Model
## Overview
The data model consists of two overlaid graphs on a set of **typed nodes**:
1. **Decomposition Tree** — strict parent→child hierarchy (DAG)
2. **Association Graph** — lateral many-to-many connections with typed edges
These are deliberately separated. The decomposition tree is the *spine* of the product. The association graph is an *overlay* that adds context without defining structure.
## Node Types
The graph has two main node types: **components** and **issues**. Components form the skeleton; issues are work attached to that skeleton.
### Component Node
A component represents a structural part of the system — a service, a module, a package, a directory. Components are relatively stable and map to code structure.
| Field | Type | Description |
|-------|------|-------------|
| `id` | UUID | Unique identifier |
| `short_id` | string | Human-readable ID (`NL-C12`) |
| `type` | enum | Always `component` |
| `title` | string | Component name |
| `description` | text | What this component does (markdown), stored in Postgres |
| `labels` | string[] | Freeform tags (`frontend`, `backend`, `infra`) |
| `owner` | actor_id? | Team or person responsible |
| `parent_id` | node_id? | Parent in decomposition tree |
| `repo_link` | repo_ref? | Linked repository or directory (see Repo Integration) |
| `created_at` | timestamp | Creation time |
| `updated_at` | timestamp | Last modification |
Components can be parents of other components (nesting: service → module → submodule) or parents of issues.
### Issue Node
An issue represents work to be done — a task, bug, feature, spike. Issues attach to components (or to the project root, or live in the inbox).
| Field | Type | Description |
|-------|------|-------------|
| `id` | UUID | Unique identifier |
| `short_id` | string | Human-readable ID (`NL-42`) |
| `type` | enum | Always `issue` |
| `title` | string | Short descriptive title |
| `description` | text | Detailed description (markdown), stored in Postgres |
| `status` | enum | Current state (see Status below) |
| `labels` | string[] | Freeform tags for orthogonal concerns |
| `assignee` | actor_id? | User or agent assigned |
| `parent_id` | node_id? | Parent in decomposition tree (null = inbox item) |
| `created_at` | timestamp | Creation time |
| `updated_at` | timestamp | Last modification |
| `created_by` | actor_id | Creator (user or agent) |
| `cycle_id` | cycle_id? | Current cycle membership |
### How They Relate
```
Project (root)
├── Component: auth-service (→ repo: github.com/team/auth)
│ ├── Component: oauth-module (→ directory: /src/oauth)
│ │ ├── Issue: implement refresh tokens
│ │ └── Issue: fix token expiry bug
│ └── Component: session-manager (→ directory: /src/sessions)
│ └── Issue: add Redis session store
├── Component: frontend-app (→ repo: github.com/team/web)
│ ├── Component: dashboard (→ directory: /src/views/dashboard)
│ └── Component: auth-ui (→ directory: /src/views/auth)
│ └── Issue: redesign login page
```
Components are the **skeleton** — stable structure that mirrors your codebase. Issues are the **work** — they flow through statuses, get assigned, join cycles. This separation is cleaner than depth-as-type because a component at depth 3 is still a component, not a "task."
### Hierarchy Rules
- Components can be children of: project root, other components
- Issues can be children of: project root, components, other issues (sub-tasks)
- Issues cannot be parents of components
- The tree remains a strict DAG — no cycles, single parent per node
### Status (Issues Only)
Default statuses (customizable per project):
- `backlog` — not yet planned
- `todo` — planned, not started
- `in_progress` — actively being worked on
- `in_review` — awaiting review
- `done` — completed
- `cancelled` — abandoned
Components don't have status — their "health" is derived from their children's statuses (e.g., "3/7 issues done, 1 blocked").
### Labels
Labels handle all classification orthogonal to hierarchy and type:
- **Kind (issues):** `bug`, `feature`, `chore`, `spike`
- **Area:** `frontend`, `backend`, `infra`, `docs`
- **Priority:** `p0`, `p1`, `p2`, `p3`
- **Custom:** anything the team defines
## Repository Integration
### Repo Link
A component can be linked to a code source:
| Field | Type | Description |
|-------|------|-------------|
| `provider` | enum | `github`, `gitlab`, `bitbucket` |
| `repo_url` | string | Repository URL |
| `path` | string? | Subdirectory within repo (null = repo root) |
| `branch` | string? | Default branch to track (null = repo default) |
### Multi-Repo Support
A project can span multiple repositories. Each repo (or subdirectory) maps to a component node:
- **Monorepo:** one repo → multiple component nodes (one per package/service)
- **Multi-repo microservices:** each repo → one top-level component node
- **Mixed:** some repos map to a single component, others are split
### Skeleton Inference (Fast-Start)
When connecting repositories, the system proposes a component tree:
1. **Connect:** OAuth via Authentik to GitHub/GitLab
2. **Select repos:** pick which repositories belong to this project
3. **Scan:** system reads repo structure — directories, package manifests (`package.json`, `pyproject.toml`, `go.mod`, `Cargo.toml`), major module boundaries
4. **Propose:** AI suggests a component tree: "I see 3 services with these modules — here's a proposed skeleton"
5. **Adjust:** user reviews, merges, splits, renames, rearranges
6. **Create:** skeleton is committed — user starts attaching issues
### Code ↔ Component Mapping
Because components are linked to repos/directories:
- **Commits** touching files in a component's path are automatically associated with that component
- **PRs** surfaced in the right component's activity feed without manual `NL-42` tagging (explicit tagging still works for issue-level linking)
- **New files/directories** can trigger suggestions: "A new directory `/src/notifications` appeared — add as a component?"
- **Agent context:** agent scoped to a component subtree can reference the actual code structure
## Decomposition Tree
The decomposition tree is a strict hierarchy:
- **Single parent:** Every node has exactly one parent (except roots)
- **Directed:** Edges flow from abstract (parent) to concrete (child)
- **Acyclic:** No node can be its own ancestor
- **Single root per project:** Each project has one root node (v0.1)
- **Typed:** Components form the skeleton, issues attach to it
### Reparenting
Moving a node to a different parent is a **structural change** (plan-then-apply):
- Permission boundaries may shift
- For components: repo link association persists, but position in tree changes
- For issues: moving between components changes which codebase the work is conceptually "in"
- System warns about consequences before committing
## Association Graph (Lateral Links)
### Link Types
| Type | Semantics | Directionality |
|------|-----------|----------------|
| `blocks` | A blocks B | Directed |
| `blocked_by` | Inverse of blocks | Directed (auto-created) |
| `relates_to` | General association | Undirected |
| `duplicates` | A is a duplicate of B | Directed |
| `depends_on` | Component A depends on component B | Directed |
`depends_on` is specifically for inter-component dependencies — can be inferred from code (import graphs, API calls) or declared manually.
### Backlinks (Inbound Link Surfacing)
Every node surfaces inbound lateral links: "3 tasks blocked by this," "2 components depend on this."
### Cross-Project Links (v0.2+)
Deferred. Will enable cross-project/cross-repo dependency tracking.
## Triage Inbox
Issues with `parent_id = null` live in the **rootless inbox**. With repo integration, triage becomes smarter — the system (or an agent) can suggest which component an inbox issue belongs to based on keywords, file paths, or related commits.
## Cycles
A cycle is a named set of issue references with a date range. Components don't join cycles — only issues do.
## Change History
Every mutation is a stored event with actor + timestamp. For components, this includes repo link changes and skeleton inference events.
## Project (v0.1)
| Field | Type | Description |
|-------|------|-------------|
| `id` | UUID | Unique identifier |
| `name` | string | Project name |
| `root_id` | node_id | Root node of decomposition tree |
| `repos` | repo_ref[] | Connected repositories |
| `created_at` | timestamp | Creation time |
| `members` | actor_id[] | Users and agents with access |

View File

@ -1,127 +0,0 @@
# Non-Linear: Data Model
## Overview
The data model consists of two overlaid graphs on the same set of nodes:
1. **Decomposition Tree** — strict parent→child hierarchy (DAG)
2. **Association Graph** — lateral many-to-many connections with typed edges
These are deliberately separated. The decomposition tree is the *spine* of the product. The association graph is an *overlay* that adds context without defining structure.
## Issue Node (Atomic Unit)
Every node in the graph is an **issue**. Issues are intentionally untyped — their depth in the decomposition tree implies their abstraction level.
| Field | Type | Description |
|-------|------|-------------|
| `id` | UUID | Unique identifier |
| `title` | string | Short descriptive title |
| `description` | text | Detailed description (markdown) |
| `status` | enum | Current state (see Status below) |
| `labels` | string[] | Freeform tags for orthogonal concerns |
| `assignee` | actor_id? | User or agent assigned |
| `parent_id` | issue_id? | Parent in decomposition tree (null = root) |
| `created_at` | timestamp | Creation time |
| `updated_at` | timestamp | Last modification |
| `created_by` | actor_id | Creator (user or agent) |
### Status
Default statuses (customizable per project):
- `backlog` — not yet planned
- `todo` — planned, not started
- `in_progress` — actively being worked on
- `in_review` — awaiting review
- `done` — completed
- `cancelled` — abandoned
### Labels
Labels handle all classification orthogonal to hierarchy:
- **Kind:** `bug`, `feature`, `chore`, `spike`
- **Area:** `frontend`, `backend`, `infra`, `docs`
- **Priority:** `p0`, `p1`, `p2`, `p3`
- **Custom:** anything the team defines
### Why Untyped
Traditional trackers have explicit types: Epic, Story, Task, Subtask, Bug. This creates rigidity — teams argue about what level something belongs at, and refactoring the hierarchy means changing types.
In Non-Linear, depth *is* type:
```
Root (depth 0) → "project level"
├── Child (depth 1) → "component/epic level"
│ ├── Child (depth 2) → "feature/story level"
│ │ └── Child (depth 3) → "task level"
```
A node's abstraction level is determined by its position, not a type field. Moving a node to a different depth changes its conceptual level automatically.
## Decomposition Tree
The decomposition tree is a strict hierarchy with these properties:
- **Single parent:** Every node has exactly one parent (except roots)
- **Directed:** Edges flow from abstract (parent) to concrete (child)
- **Acyclic:** No node can be its own ancestor
- **Single root per project:** Each project has one root node (v0.1)
### Reparenting
Moving a node to a different parent is a **structural change**. It affects:
- The node's implied abstraction level
- Agent permissions (if scoped to a subtree)
- The layered view layout
- Who can access the node (if resource-scoped policies apply)
The system should warn when reparenting changes permission boundaries.
## Association Graph (Lateral Links)
Lateral links are typed, many-to-many relationships that don't affect hierarchy.
### Link Types
| Type | Semantics | Directionality |
|------|-----------|----------------|
| `blocks` | A blocks B (B can't proceed until A is done) | Directed |
| `blocked_by` | Inverse of blocks | Directed (auto-created) |
| `relates_to` | General association | Undirected |
| `duplicates` | A is a duplicate of B | Directed |
### Cross-Project Links (v0.2+)
In future versions, lateral links can cross project boundaries. This enables:
- Freelancer's client project depending on a shared library
- Shared infrastructure components blocking multiple projects
- Cross-team dependency tracking
Cross-project links add complexity to permissions (an agent scoped to Project A sees a link to Project B but may not access the target) and are deferred to v0.2.
### Link vs. Parent Relationship
| Property | Parent→Child | Lateral Link |
|----------|-------------|--------------|
| Cardinality | One parent per node | Many-to-many |
| Affects hierarchy | Yes | No |
| Affects layered view | Yes | Shown as overlay edges |
| Affects permissions | Yes (subtree scoping) | No |
| Removal impact | Structural (node needs new parent) | Annotation only |
## Project (v0.1)
| Field | Type | Description |
|-------|------|-------------|
| `id` | UUID | Unique identifier |
| `name` | string | Project name |
| `root_id` | issue_id | Root node of decomposition tree |
| `created_at` | timestamp | Creation time |
| `members` | actor_id[] | Users and agents with access |
In v0.1, each project has exactly one root node and one decomposition tree. Multi-root or cross-project features are deferred.

126
03-VIEWS-AND-UI.md Normal file
View File

@ -0,0 +1,126 @@
# Non-Linear: Views & UI
## Overview
Non-Linear provides two primary graph views, a flat fallback, and a cycle view. The interaction model is keyboard-first with a command palette as the primary navigation method.
## View 1: Layered Overview
**Purpose:** See the entire project structure at a glance, organized by abstraction level.
**Layout:** Rows arranged by depth in the decomposition tree. Each row represents one abstraction layer.
```
Layer 0 (root): [My SaaS Project]
/ | \
Layer 1: [Auth] [Billing] [Dashboard]
/ \ | / \
Layer 2: [Login] [SSO] [Stripe] [Charts] [Filters]
| |
Layer 3: [OAuth flow] [Bar chart component]
```
### Behavior
- **Edges visible:** Parent→child edges drawn between layers. Lateral links shown as overlay edges (dashed, colored by type).
- **Backlink count badges:** Nodes show inbound link counts ("3 blocked").
- **Cycle highlighting:** Cycle members highlighted with color/tag.
- **Collapse/expand:** Each node can collapse its subtree.
- **Zoom:** At high zoom, nodes show only title. At lower zoom, nodes show title + status + assignee.
- **Filtering:** Filter by label, status, assignee. Non-matching nodes dimmed, not hidden (preserving structural context).
- **Node coloring:** Status-based by default. Optionally by label, assignee, or priority.
## View 2: Focus Widget
**Purpose:** Inspect a single node and its immediate context.
**Layout:** Selected node in center. Parent shown above. Children shown below. Lateral links shown as badges or side panel.
### Behavior
- **Breadcrumb navigation:** Full root→node path as clickable segments at the top (e.g., `Project > Backend > Auth > Login Flow > Password Reset`).
- **Navigation:** Click any node to make it the new focus ("walking the tree").
- **Detail panel:** Full description, comments, change history timeline (compact, filterable by event type).
- **Backlinks section:** Inbound lateral links grouped by type.
- **Quick actions:** Change status, assign, add labels, add child, add link.
## View 3: Flat List / Board (Fallback)
**Purpose:** Quick triage, standup-style review, batch operations.
**Layout:** Standard list or Kanban board (columns = statuses).
- **Sorting:** By status, priority, assignee, updated date, creation date.
- **Grouping:** By parent node, label, assignee.
- **Bulk actions:** Multi-select for status change, assign, add labels.
- **Search:** Full-text search across titles and descriptions.
## View 4: Cycle View
**Purpose:** See current sprint/cycle work across all subtrees.
**Layout:** Flat list of current cycle members, grouped by subtree or status.
## Interaction Model
### Keyboard-First
Every action has a keyboard shortcut. Graph navigation:
- `Alt+↑` — navigate to parent
- `Alt+↓` — navigate to children
- `Alt+←/→` — navigate siblings
- Follow lateral links via shortcut
### Command Palette (`Cmd+K`)
The primary navigation and action method:
- Fuzzy search across all nodes, actions, views
- Context-aware: shows different actions depending on current view and selection
- Unified entry point for commands ("change status") and search results ("issues matching 'stripe'")
### Natural Language Node Creation (from Todoist)
Type in the command palette: "Add a bug under Auth > Login flow: password reset emails aren't sending, p0, assign to agent-1"
- Parses tree path via `>`, labels via `#` or keywords (`p0`, `bug`), assignment via `@`
- Fuzzy-matches node names in the path
- Falls back to inbox if path can't be resolved
- Agent can use same syntax via API
### Plan-Then-Apply
Structural changes show a preview before committing:
- **Reparent preview:** "Moving 'Auth' under 'Infrastructure' will: change depth from 1→2, move 12 child nodes, agent-1 will lose access (scoped to 'Backend' subtree)"
- **Bulk operations:** "Changing status of 8 nodes to 'done' — 3 have open blockers"
- **Delete preview:** "Deleting 'Legacy API' will remove 1 node + 7 children, break 3 lateral links"
- **Agent proposals:** agent submits a "plan" (e.g., proposed decomposition), human reviews diff-style and approves
## View Switching
Users can switch between views freely. The selected node context is preserved — if you're focused on "Login" in the Focus Widget and switch to Layered Overview, "Login" is highlighted/centered.
## Notifications (Scoped Watching)
- **Watch granularity:** single node, subtree, or project-wide
- **Event type filters:** status changes, comments, new children, link changes, agent actions
- **Defaults:** watch nodes you created or are assigned to
- **Mute:** temporarily silence a noisy subtree
- **Agent filter:** "only notify me when agent-1 changes something"
## Entry Experience
### New Project (Clean Start)
1. User creates a project → automatically creates root node with project name
2. User placed in Focus Widget on root node
3. Prompt to add first children (components/areas)
4. Command palette available immediately for keyboard-driven creation
### Import (v0.2+)
- Import from Linear, Jira, GitHub Issues
- System infers hierarchy from existing relationships
- User reviews and adjusts inferred tree before confirming

View File

@ -1,104 +0,0 @@
# Non-Linear: Views & UI
## Overview
Non-Linear provides two primary graph views plus a flat fallback. The graph views test the core thesis (top-down decomposition helps people think about projects). The flat view ensures daily workflows aren't compromised.
## View 1: Layered Overview
**Purpose:** See the entire project structure at a glance, organized by abstraction level.
**Layout:** Rows arranged by depth in the decomposition tree. Each row represents one abstraction layer.
```
Layer 0 (root): [My SaaS Project]
/ | \
Layer 1: [Auth] [Billing] [Dashboard]
/ \ | / \
Layer 2: [Login] [SSO] [Stripe] [Charts] [Filters]
| |
Layer 3: [OAuth flow] [Bar chart component]
```
### Behavior
- **Edges visible:** Parent→child edges are drawn between layers. Lateral links (blocks, relates-to) shown as overlay edges with different styling (dashed, colored by type).
- **Collapse/expand:** Each node can collapse its subtree to reduce visual noise.
- **Zoom:** The view supports zoom in/out. At high zoom, nodes show only title. At lower zoom, nodes show title + status + assignee.
- **Filtering:** Filter by label, status, assignee to highlight relevant subsets. Non-matching nodes are dimmed, not hidden (preserving structural context).
- **Node coloring:** Status-based coloring by default. Optionally color by label, assignee, or priority.
### What This View Is Good For
- Understanding overall project structure
- Identifying structural gaps (a component with no tasks beneath it)
- Seeing where work is concentrated (many in-progress nodes in one subtree)
- Onboarding new team members to a project
- Agent overview: "show me the full graph"
## View 2: Focus Widget
**Purpose:** Inspect a single node and its immediate context — parent and direct children.
**Layout:** Selected node in the center. Parent shown above. Children shown below. Lateral links shown as badges or a side panel.
```
[Parent: Auth]
|
──► [Selected: Login] ◄── blocked by [Stripe integration]
/ \
[OAuth flow] [Email/pass form]
```
### Behavior
- **Navigation:** Click any node to make it the new focus. This enables "walking the tree" up and down.
- **Detail panel:** The selected node's full detail (description, comments, history, links) is visible in a side or bottom panel.
- **Quick actions:** Change status, assign, add labels, add child, add link — all from this view.
- **Lateral links:** Shown as a list/badge on the selected node: "Blocked by: [Stripe integration]", "Relates to: [Dashboard filters]".
### What This View Is Good For
- Daily work: understanding what's above and below your current task
- Breaking down work: creating children from a component node
- Reviewing context: what blocks this? what depends on this?
- Agent focus: "what are the children of Backend API?"
## View 3: Flat List / Board (Fallback)
**Purpose:** Quick triage, standup-style review, batch operations.
**Layout:** Standard list or Kanban board (columns = statuses). No graph structure visible.
### Behavior
- **Sorting:** By status, priority, assignee, updated date, creation date.
- **Grouping:** By parent node, label, assignee. Grouping by parent gives a lightweight hierarchy without the full graph view.
- **Bulk actions:** Multi-select to change status, assign, add labels.
- **Search:** Full-text search across titles and descriptions.
### What This View Is Good For
- Morning triage: "what's in review? what's blocked?"
- Standup: quick scan of in-progress items
- Batch operations: close all done items, reassign a set of tasks
- Users who prefer traditional workflows
## View Switching
Users can switch between views freely. The selected node context is preserved when switching — if you're focused on "Login" in the Focus Widget and switch to Layered Overview, "Login" is highlighted/centered in the graph.
## Entry Experience
### New Project (Clean Start)
1. User creates a project → automatically creates a root node with the project name
2. User is placed in the Focus Widget on the root node
3. Prompt to add first children (components/areas of the project)
4. As children are added, the Layered Overview becomes useful
### Import (v0.2+)
- Import from Linear, Jira, GitHub Issues
- System infers hierarchy from existing relationships (epics→stories→tasks)
- User reviews and adjusts the inferred tree before confirming

View File

@ -8,7 +8,7 @@ Non-Linear uses a granular, policy-based permission system inspired by AWS S3/IA
### Policy ### Policy
A policy is a single permission rule with three dimensions: A policy is a single permission rule with four dimensions:
``` ```
Policy = Actor + Action + Resource Scope + Effect Policy = Actor + Action + Resource Scope + Effect
@ -49,7 +49,7 @@ Policy = Actor + Action + Resource Scope + Effect
| **Subtree** | A specific node and all its descendants | Agent confined to "Backend" component | | **Subtree** | A specific node and all its descendants | Agent confined to "Backend" component |
| **Single Node** | Exactly one node | Lock down a sensitive design doc | | **Single Node** | Exactly one node | Lock down a sensitive design doc |
**Subtree scoping is the key feature.** Because the data model is a decomposition tree, resource scope maps naturally to graph position. "Backend API subtree" means "this node and everything decomposed beneath it." The graph *is* the namespace — no separate resource-naming scheme needed. **Subtree scoping is the key feature.** Because the data model is a decomposition tree, resource scope maps naturally to graph position. The graph *is* the namespace — no separate resource-naming scheme needed.
## Roles ## Roles
@ -59,8 +59,6 @@ A role is a named, reusable collection of policies. Roles are editable — the d
#### Owner #### Owner
Full control over the project.
```yaml ```yaml
role: owner role: owner
policies: policies:
@ -71,8 +69,6 @@ policies:
#### Member #### Member
Standard team member. Can do everything except manage policies and delete nodes.
```yaml ```yaml
role: member role: member
policies: policies:
@ -85,8 +81,6 @@ policies:
#### Agent Reader #### Agent Reader
Default agent role. Read-only plus comments and status changes.
```yaml ```yaml
role: agent-reader role: agent-reader
policies: policies:
@ -99,8 +93,6 @@ policies:
#### Backend Decomposer Agent #### Backend Decomposer Agent
An AI agent that can break down backend components into tasks, but only within the Backend subtree.
```yaml ```yaml
role: backend-decomposer role: backend-decomposer
policies: policies:
@ -108,14 +100,12 @@ policies:
scope: global scope: global
effect: allow effect: allow
- action: [create_child, add_label, add_comment] - action: [create_child, add_label, add_comment]
scope: subtree("Backend API") # scoped to node ID in practice scope: subtree("Backend API")
effect: allow effect: allow
``` ```
#### Triage Agent #### Triage Agent
An agent that can reprioritize and reassign, but can't create or delete.
```yaml ```yaml
role: triage-agent role: triage-agent
policies: policies:
@ -125,72 +115,25 @@ policies:
effect: allow effect: allow
``` ```
#### External Reviewer
A stakeholder who can view and comment on high-level nodes only.
```yaml
role: external-reviewer
policies:
- action: [read_node, read_comments, add_comment]
scope: subtree("root") # top two levels only (enforced by depth limit)
effect: allow
```
## Resolution Order ## Resolution Order
When multiple policies apply to an actor+action+resource combination:
1. **Direct policy on actor** takes precedence over role-inherited policies 1. **Direct policy on actor** takes precedence over role-inherited policies
2. **Deny always wins** over allow at the same level 2. **Deny always wins** over allow at the same level
3. **Narrower scope wins** over broader scope (single node > subtree > global) 3. **Narrower scope wins** over broader scope (single node > subtree > global)
4. **Default: deny** — if no policy explicitly allows an action, it is denied 4. **Default: deny** — if no policy explicitly allows an action, it is denied
### Resolution Examples
```
Scenario: Agent "deploy-bot" has role "agent-reader" (global read + comment + status)
AND a direct policy: deny change_status on "Production Deploy" node
Action: deploy-bot tries to change status on "Production Deploy"
Result: DENIED — direct deny on single node overrides role's global allow
Action: deploy-bot tries to change status on "Backend API / Auth"
Result: ALLOWED — role's global allow applies, no deny in scope
```
```
Scenario: Agent "decomposer" has role "backend-decomposer"
(global read, create_child on Backend subtree)
Action: decomposer tries to create a child under "Frontend / Charts"
Result: DENIED — create_child only allowed in Backend subtree
Action: decomposer tries to read "Frontend / Charts"
Result: ALLOWED — read_node is global scope
```
## Reparenting and Permission Changes ## Reparenting and Permission Changes
When a node is moved to a different parent: When a node is moved to a different parent (via plan-then-apply):
1. All subtree-scoped policies are re-evaluated against the new position 1. All subtree-scoped policies are re-evaluated against the new position
2. Actors may gain or lose access depending on the new subtree 2. Actors may gain or lose access depending on the new subtree
3. The system should warn the user: *"Moving this node will change who can access it"* 3. System warns: *"Moving this node will change who can access it"*
4. Specifically flag if any agent loses access to nodes it's currently assigned to 4. Specifically flags if any agent loses access to nodes it's currently assigned to
## Lateral Links Crossing Permission Boundaries ## Lateral Links Crossing Permission Boundaries
When a lateral link connects two nodes and the actor only has access to one: When a lateral link connects two nodes and the actor only has access to one:
- **Link visible, target opaque.** The actor can see that a link exists and its type ("blocked by node X") but cannot read the target node's details. - **Link visible, target opaque.** The actor can see that a link exists and its type but cannot read the target node's details.
- This enables agents to reason about blockers without leaking information across permission boundaries. - This enables agents to reason about blockers without leaking information across permission boundaries.
## Implementation Notes for v0.1
- Build the full granular policy engine underneath
- Ship the three default presets (owner, member, agent-reader)
- Expose custom role creation through a settings panel (can be minimal UI)
- Store policies as structured data (JSON/YAML), not code
- Policy evaluation should be centralized — every API call goes through the policy engine
- Audit log: record every policy evaluation for debugging and compliance

View File

@ -10,78 +10,66 @@ Non-Linear treats AI agents as first-class actors — not assistants bolted onto
The agent operates independently within the project graph. It has its own account, picks up tasks, reports status, and navigates the decomposition tree autonomously. The agent operates independently within the project graph. It has its own account, picks up tasks, reports status, and navigates the decomposition tree autonomously.
**Primary use case:** One-person team (or small team) where an AI agent acts as a focused collaborator. **Primary use case:** One-person team where an AI agent acts as a focused collaborator.
**How the graph helps:** The decomposition tree gives the agent *rails*. Instead of wandering through a flat list, the agent traverses a topology: **How the graph helps:** The decomposition tree gives the agent *rails*:
- "What's the highest-priority unblocked leaf node in Backend?" → graph traversal query - "What's the highest-priority unblocked leaf node in Backend?" → graph traversal query
- "What's the status of the Auth component?" → read the subtree, aggregate child statuses - "What's the status of the Auth component?" → read subtree, aggregate child statuses
- "What should I work on next?" → find unblocked, assigned, in-progress leaves - "What should I work on next?" → find unblocked, assigned, in-progress leaves
**v0.1 capabilities:**
- Read any node (subject to policy)
- Post comments (status updates, analysis, questions)
- Change status on assigned nodes
- Traverse the tree (get parent, get children, get subtree)
- Query lateral links (what blocks this? what does this relate to?)
**Deferred (v0.2+):**
- Create child nodes (task decomposition)
- Propose reparenting
- Create lateral links
- Autonomous task pickup (self-assign from backlog)
### Agent as Assistant ### Agent as Assistant
The agent helps human users by analyzing the graph and suggesting actions. It doesn't act autonomously — it provides recommendations that humans execute. The agent helps human users by analyzing the graph and suggesting actions.
**Primary use case:** Teams where AI augments human decision-making. **Primary use case:** Teams where AI augments human decision-making.
**Examples:** **Examples:**
- "Break this component into tasks" → agent suggests a set of child nodes, human approves - "Break this component into tasks" → agent suggests child nodes, human approves
- "What's blocking the Auth epic?" → agent traverses the subtree and lateral links, reports findings - "What's blocking the Auth epic?" → agent traverses subtree and lateral links
- "Summarize progress on Dashboard this week" → agent reads subtree statuses and recent comments - "Summarize progress on Dashboard this week" → agent reads subtree statuses and recent comments
- "Are there any orphaned tasks?" → agent finds leaf nodes with no clear connection to higher-level goals - "Suggest priority order for these tasks" → agent considers dependencies and blockers
- "Suggest priority order for these tasks" → agent considers dependencies, blockers, and relationships
## Agent API (v0.1) ## Agent API (v0.1)
The agent API is a subset of the main API, governed by the same policy engine. Agents authenticate with API tokens tied to their actor account. Governed by the same policy engine as human users. Agents authenticate with API tokens tied to their actor account.
### Endpoints ### Graph Traversal
#### Graph Traversal
``` ```
GET /api/v1/nodes/{id} # Read single node GET /api/v1/nodes/{id} # Read single node
GET /api/v1/nodes/{id}/children # Direct children GET /api/v1/nodes/{id}/children # Direct children
GET /api/v1/nodes/{id}/subtree # Full subtree (depth-limited) GET /api/v1/nodes/{id}/subtree # Full subtree (depth-limited)
GET /api/v1/nodes/{id}/parent # Parent node GET /api/v1/nodes/{id}/parent # Parent node
GET /api/v1/nodes/{id}/links # Lateral links (blocks, relates-to) GET /api/v1/nodes/{id}/links # Outbound lateral links
GET /api/v1/nodes/{id}/backlinks # Inbound lateral links
GET /api/v1/nodes/{id}/path # Path from root to this node GET /api/v1/nodes/{id}/path # Path from root to this node
GET /api/v1/nodes/{id}/history # Change history timeline
GET /api/v1/projects/{id}/root # Project root node GET /api/v1/projects/{id}/root # Project root node
GET /api/v1/projects/{id}/inbox # Triage inbox (rootless nodes)
``` ```
#### Queries ### Queries
``` ```
GET /api/v1/projects/{id}/nodes?status=in_progress&assignee=agent-123 GET /api/v1/projects/{id}/nodes?status=in_progress&assignee=agent-123
GET /api/v1/projects/{id}/nodes?label=bug&status=backlog GET /api/v1/projects/{id}/nodes?label=bug&status=backlog
GET /api/v1/projects/{id}/nodes?unblocked=true&status=todo GET /api/v1/projects/{id}/nodes?unblocked=true&status=todo
GET /api/v1/projects/{id}/nodes?cycle=current
``` ```
#### Actions (Write) ### Write Actions (v0.1)
``` ```
POST /api/v1/nodes/{id}/comments # Add comment POST /api/v1/nodes/{id}/comments # Add comment
PATCH /api/v1/nodes/{id}/status # Change status PATCH /api/v1/nodes/{id}/status # Change status
PATCH /api/v1/nodes/{id}/parent # Triage: assign parent to inbox item
``` ```
#### Deferred Actions (v0.2+) ### Deferred Actions (v0.2+)
``` ```
POST /api/v1/nodes/{id}/children # Create child node POST /api/v1/nodes/{id}/children # Create child node
PATCH /api/v1/nodes/{id}/parent # Reparent
POST /api/v1/nodes/{id}/links # Create lateral link POST /api/v1/nodes/{id}/links # Create lateral link
DELETE /api/v1/nodes/{id}/links/{link_id} # Remove lateral link DELETE /api/v1/nodes/{id}/links/{link_id} # Remove lateral link
PATCH /api/v1/nodes/{id} # Edit node details PATCH /api/v1/nodes/{id} # Edit node details
@ -92,21 +80,26 @@ PATCH /api/v1/nodes/{id} # Edit node details
- Agents authenticate via API tokens (bearer tokens) - Agents authenticate via API tokens (bearer tokens)
- Each token is tied to an actor (agent account) - Each token is tied to an actor (agent account)
- The actor has roles/policies assigned via the policy engine - The actor has roles/policies assigned via the policy engine
- Token scoping: a token can optionally be restricted to a subset of the agent's permissions (defense in depth) - Token scoping: a token can optionally be restricted to a subset of the agent's permissions
### Response Format ### Response Format
All responses include the agent's effective permissions on the returned resource: All responses include effective permissions on the returned resource:
```json ```json
{ {
"node": { "node": {
"id": "uuid-123", "id": "uuid-123",
"short_id": "NL-42",
"title": "Login component", "title": "Login component",
"status": "in_progress", "status": "in_progress",
"labels": ["frontend", "p1"], "labels": ["frontend", "p1"],
"parent_id": "uuid-456", "parent_id": "uuid-456",
"children_count": 3, "children_count": 3,
"backlinks": {
"blocked_by_count": 1,
"relates_to_count": 2
},
"links": [ "links": [
{ {
"type": "blocked_by", "type": "blocked_by",
@ -131,14 +124,10 @@ All responses include the agent's effective permissions on the returned resource
} }
``` ```
Note: When a lateral link target is outside the agent's permission scope, `target_accessible` is `false` and details are withheld. The agent knows a link exists (and its type) but can't read the target.
## Agent Design Patterns ## Agent Design Patterns
### Focused Worker Agent ### Focused Worker Agent
An agent assigned to a specific subtree that processes tasks sequentially.
``` ```
Loop: Loop:
1. Query subtree for unblocked, assigned, todo items 1. Query subtree for unblocked, assigned, todo items
@ -147,47 +136,39 @@ Loop:
4. Do work (external to tracker) 4. Do work (external to tracker)
5. Post comment with results 5. Post comment with results
6. Change status to done or in_review 6. Change status to done or in_review
7. Repeat
``` ```
### Triage Agent ### Triage Agent
An agent that scans the full project and suggests prioritization.
``` ```
Loop: Loop:
1. Query all nodes with status = backlog 1. Query inbox for unplaced nodes
2. For each, check lateral links (blockers, dependencies) 2. For each, analyze title/description and match to existing subtrees
3. Post comment with suggested priority and rationale 3. Assign parent (place in tree)
4. Optionally: change status from backlog to todo for approved items 4. Post comment with rationale
``` ```
### Decomposition Agent (v0.2+) ### Decomposition Agent (v0.2+)
An agent that breaks high-level components into tasks.
``` ```
Triggered when: a node has label "needs-decomposition" Triggered when: a node has label "needs-decomposition"
1. Read the node's title, description, and context (parent, siblings) 1. Read node context (parent, siblings, description)
2. Propose a set of child nodes (via comment or API) 2. Propose child nodes via comment or plan-then-apply
3. Human reviews and approves 3. Human reviews and approves
4. Agent creates the children 4. Agent creates children
5. Remove "needs-decomposition" label
``` ```
## Integration Points ## Webhooks (v0.1)
### Webhooks (v0.1) Events emitted:
The tracker emits webhook events for:
- `node.status_changed` - `node.status_changed`
- `node.comment_added` - `node.comment_added`
- `node.created` - `node.created`
- `node.link_created` - `node.link_created`
- `node.reparented`
- `node.cycle_added`
Agents can subscribe to webhooks to react to changes in real-time. ## MCP Compatibility (v0.2+)
### MCP (Model Context Protocol) Compatibility (v0.2+) Expose the Non-Linear API as an MCP server so AI agents built on LLM frameworks can connect natively.
Expose the Non-Linear API as an MCP server so AI agents built on LLM frameworks can connect natively without custom integration code.

140
06-SCOPE-AND-ROADMAP.md Normal file
View File

@ -0,0 +1,140 @@
# Non-Linear: v0.1 Scope & Roadmap
## v0.1 Validation Goal
> Can a team of 3-5 people use this to run an actual project for two weeks without falling back to another tool?
If yes, the core thesis holds. If they keep opening Linear or a spreadsheet for certain tasks, those gaps tell us what v0.2 needs.
## v0.1 Scope: What Ships
### Core Data Model
- **Two node types:** components (skeleton, map to code) and issues (work, flow through statuses)
- **Decomposition tree:** strict parent→child hierarchy, single root per project
- **Lateral links:** blocks, blocked_by, relates_to, duplicates, depends_on (inter-component)
- **Component health:** derived from children's statuses (no status field on components)
- **Single project** (no cross-project features)
- **Complete change history:** every mutation is a stored event with actor + timestamp
### Repository Integration
- **Multi-repo support:** connect multiple GitHub/GitLab repos to one project
- **Repo ↔ component linking:** each repo or subdirectory maps to a component node
- **Monorepo support:** one repo → multiple components (per package/service)
- **Skeleton inference (fast-start):** connect repos → scan structure → AI proposes component tree → user adjusts → skeleton committed
- **Automatic commit/PR association:** code activity auto-surfaces in the right component's feed
- **New directory detection:** suggest adding new components when code structure changes
### Views
- **Layered overview:** rows by depth, edges visible, lateral links as overlay, collapse/expand, filtering, backlink count badges, component vs. issue visual distinction
- **Focus widget:** node + parent + children, detail panel, quick actions, backlinks, breadcrumbs, change history, repo link info for components
- **Flat list/board:** Kanban by status (issues only), sorting, grouping, bulk actions, search
- **Cycle view:** current cycle issues, grouped by component or status
### Interaction Model
- **Keyboard-first:** every action has a shortcut, graph navigation via `Alt+↑/↓/←/→`
- **Command palette (`Cmd+K`):** fuzzy search across nodes, actions, views; context-aware
- **Natural language node creation:** parse tree path, labels, assignment from text
- **Plan-then-apply:** structural changes show preview of consequences before committing
### Triage
- **Rootless inbox:** issues without a parent land here
- **Smart triage:** system suggests which component an issue belongs to based on keywords, file paths, or related commits
- **Triage = assigning a parent**, placing the issue in the tree
- Agents can triage via API
### Cycles
- **Cycle:** named set of issue references + date range
- Cycle membership shown as color highlight/tag in all views
- Only issues join cycles (not components)
- Agent scoping: policies can restrict agents to "current cycle only"
### Notifications
- **Scoped watching:** single node, subtree, or project-wide
- **Event type filters:** status changes, comments, new children, link changes, agent actions, code activity
- **Defaults:** watch nodes you created or are assigned to
- **Mute:** temporarily silence a subtree
### Code Integration
- **Node ID format:** short, typeable (`NL-42` for issues, `NL-C12` for components)
- **Commit message parsing:** `fixes NL-42` triggers status change
- **PR linkage:** PR appears in relevant node's activity feed (auto via component path, or explicit via ID)
- **Branch naming support:** `feature/NL-42-login-flow`
- **Webhooks from GitHub/GitLab** trigger node updates
### Policy Engine
- **Full granular engine:** policy = actor + action + resource scope + effect
- **Three resource scopes:** global, subtree, single node
- **Three default presets:** owner, member, agent-reader (all editable)
- **Custom role creation:** via settings panel
- **Resolution order:** direct > role, deny > allow, narrow > broad, default deny
### Agent API
- **Authentication:** API tokens tied to agent actor accounts
- **Read operations:** node, children, subtree, parent, links, backlinks, path, history, root, inbox, component repo info
- **Write operations:** comment, change status, triage (assign parent)
- **Queries:** filter by status, assignee, labels, unblocked, cycle, node type
- **Permission-aware responses:** include effective permissions, opaque link targets
- **Webhooks:** node.status_changed, node.comment_added, node.created, node.link_created, node.reparented, node.cycle_added, repo.commit_associated
### Entry Experience — Three Paths
1. **Connect your code (new):** OAuth to GitHub/GitLab → select repos → AI scans and proposes component skeleton → user adjusts → start adding issues
2. **Clean start:** Create project → root node → add components and issues manually
3. **Import from tracker (v0.2+):** Import from Linear/Jira/GitHub Issues → infer hierarchy
## What's Deferred
### v0.2: Expand Agent Capabilities + Cross-Project
- [ ] Agent creates child nodes (task decomposition)
- [ ] Agent creates lateral links
- [ ] Agent proposes reparenting (submit as "plan" for human review)
- [ ] Cross-project lateral links
- [ ] Cross-project agent scoping
- [ ] Multi-root projects
- [ ] Import from Linear / Jira / GitHub Issues (infer tree levels)
- [ ] Dependency inference from code (import graphs → `depends_on` links)
### v0.3+: Integrations & Intelligence
- [ ] Auto-link external signals (Sentry/PagerDuty errors → right component subtree)
- [ ] MCP server exposure
- [ ] Agent autonomous task pickup (self-assign from backlog)
- [ ] Time tracking
- [ ] Custom views / saved filters
- [ ] Export (PDF, CSV)
### Deliberately Not Building
- Rich document blocks (Notion-style) — stay focused on tracking
- Multiplayer presence / cursors — overkill for small teams
- Canvas sketch-to-structure — cool demo, unclear daily value
## Open Questions
1. **Graph rendering library:** D3 vs Cytoscape vs custom WebGL?
2. **Real-time updates:** WebSocket via FastAPI or dedicated service (Centrifugo)?
3. **Cycle UX:** How to add/remove issues from cycles?
4. **Natural language parsing:** LLM-powered or rule-based for v0.1?
5. **Git integration depth:** Webhook-only, or GitHub App with deeper permissions?
6. **Skeleton inference depth:** How deep should auto-scan go? Top-level packages only, or nested modules?
7. **Component health aggregation:** Simple ratios, or weighted by priority/blockers?
## Success Metrics
- **Adoption:** Team uses Non-Linear as sole tracker for 2+ weeks
- **Retention:** No fallback to Linear/spreadsheet for any workflow
- **Repo connection rate:** >50% of projects connect at least one repo
- **Agent utility:** Agent produces at least 1 useful action per day
- **Graph value:** Users create trees with 3+ depth levels
- **Speed:** Command palette action <200ms, view transitions <100ms

View File

@ -1,105 +0,0 @@
# Non-Linear: v0.1 Scope & Roadmap
## v0.1 Validation Goal
> Can a team of 3-5 people use this to run an actual project for two weeks without falling back to another tool?
If yes, the core thesis holds. If they keep opening Linear or a spreadsheet for certain tasks, those gaps tell us what v0.2 needs.
## v0.1 Scope: What Ships
### Core Data Model ✅
- **Issue nodes:** title, description, status, labels, assignee, timestamps
- **Decomposition tree:** strict parent→child hierarchy, single root per project
- **Lateral links:** blocks, blocked_by, relates_to, duplicates
- **Untyped nodes:** depth = abstraction level, labels = orthogonal classification
- **Single project** (no cross-project features)
### Views ✅
- **Layered overview:** rows by depth, parent→child edges visible, lateral links as overlay, collapse/expand, filtering
- **Focus widget:** selected node + parent + children, detail panel, quick actions, lateral links shown
- **Flat list/board:** Kanban columns by status, sorting, grouping, bulk actions, search
### Policy Engine ✅
- **Full granular engine underneath:** policy = actor + action + resource scope + effect
- **Three resource scopes:** global, subtree, single node
- **Three default presets:** owner, member, agent-reader (all editable)
- **Custom role creation:** via settings panel (can be minimal UI)
- **Resolution order:** direct > role, deny > allow, narrow > broad, default deny
- **Reparenting warnings:** surface when moving a node changes permission boundaries
### Agent API ✅
- **Authentication:** API tokens tied to agent actor accounts
- **Read operations:** get node, children, subtree, parent, links, path, root
- **Write operations:** add comment, change status
- **Queries:** filter by status, assignee, labels, unblocked
- **Permission-aware responses:** include effective permissions, opaque link targets
- **Webhooks:** node.status_changed, node.comment_added, node.created, node.link_created
### Entry Experience ✅
- New project → auto-create root node → Focus Widget on root → prompt to add children
- Clean, fast onboarding: create root, add first children, start working
## v0.1 Scope: What's Deferred
### v0.2: Expand Agent Capabilities + Cross-Project
- [ ] Agent can create child nodes (task decomposition)
- [ ] Agent can create lateral links
- [ ] Agent can propose reparenting
- [ ] Cross-project lateral links
- [ ] Cross-project views
- [ ] Permission model for cross-project boundaries
- [ ] Import from Linear, Jira, GitHub Issues (with hierarchy inference)
- [ ] Export to standard formats
- [ ] MCP server compatibility
### v0.3: Collaboration & Intelligence
- [ ] Agent self-assignment (autonomous task pickup)
- [ ] Sprint/iteration planning view
- [ ] Time tracking
- [ ] Notifications and subscriptions
- [ ] Activity feed per node/subtree
- [ ] Agent marketplace (pre-built agent configurations)
- [ ] Graph analytics (bottleneck detection, progress heatmaps)
- [ ] AI-suggested decomposition (assistant mode: "break this into tasks")
### v0.4+: Platform
- [ ] Custom fields on nodes
- [ ] Workflow automation (status change triggers)
- [ ] Git integration (link commits to nodes)
- [ ] CI/CD integration (agent reports build status)
- [ ] Public API for third-party integrations
- [ ] Multi-workspace / organization support
- [ ] Mobile app
## Open Questions
1. **Graph rendering library:** What to use for the layered overview? Options include D3.js, Cytoscape.js, or a custom SVG renderer. D3 offers the most control; Cytoscape has built-in graph layouts.
2. **Real-time collaboration:** Do we need real-time updates in v0.1 (WebSocket) or is polling acceptable? Real-time matters more once agents are actively changing things.
3. **Offline support:** Is this needed for v0.1? Probably not — web-first is fine for the target audience.
4. **Hosting model:** SaaS-first? Self-hosted option for teams that want it? Open-source core?
5. **Node depth limits:** Should we enforce a maximum tree depth? Too deep gets unwieldy. A soft limit of 5-6 levels with a warning might work.
6. **Bulk operations in the graph view:** How do you multi-select in a graph? The flat list handles this, but power users might want it in the layered view too.
7. **History/undo:** How far back can you undo? Is there version history per node? Per graph structure? This matters for agent trust — if an agent messes up, how do you roll back?
## Success Metrics (v0.1)
- **Adoption:** Team uses Non-Linear as primary tracker for 2+ weeks
- **Graph usage:** Users spend >30% of time in graph views (not just flat list)
- **Agent integration:** At least one agent connected and actively reading/commenting
- **Retention signal:** Team continues using after initial trial without reverting to previous tool
- **Structural depth:** Average project tree depth >2 (indicating real decomposition, not flat usage)

View File

@ -0,0 +1,119 @@
# Non-Linear: Glossary & Key Decisions
## Glossary
| Term | Definition |
|------|-----------|
| **Node** | Any entity in the graph — either a component or an issue. |
| **Component** | A structural node representing part of the system (service, module, directory). Maps to code. |
| **Issue** | A work node representing something to be done (task, bug, feature). Flows through statuses. |
| **Decomposition Tree** | Strict parent→child hierarchy. The structural spine of a project. |
| **Lateral Link** | Typed edge between nodes that doesn't affect hierarchy. Types: blocks, relates_to, duplicates, depends_on. |
| **Backlink** | Inbound lateral link surfaced on a node ("3 tasks blocked by this"). |
| **Repo Link** | Association between a component node and a repository/directory in a VCS. |
| **Skeleton** | The component tree — stable structure that mirrors the codebase. |
| **Skeleton Inference** | Fast-start: connect repos, AI proposes component tree from code structure. |
| **Layered Overview** | Primary graph view showing nodes in rows by tree depth. |
| **Focus Widget** | Detail view showing one node with parent, children, breadcrumbs, and history. |
| **Actor** | Any entity that can perform actions: human user or AI agent. |
| **Policy** | Single permission rule: actor + action + resource scope + effect (allow/deny). |
| **Role** | Named, reusable bundle of policies. Convenience layer over granular engine. |
| **Resource Scope** | Where a policy applies: global, subtree, or single node. |
| **Subtree** | A node and all of its descendants in the decomposition tree. |
| **Reparenting** | Moving a node to a different parent. Structural change that affects permissions. |
| **Opaque Link** | Lateral link whose target is outside actor's permission scope. Link visible, details withheld. |
| **Root Node** | Top-level node of a project's decomposition tree. |
| **Depth** | Node's distance from root. Root = 0, its children = 1, etc. |
| **Inbox** | Staging area for issues without a parent. Triage = assigning a parent. |
| **Cycle** | Named set of issue references + date range. Temporal view over spatial tree. |
| **Plan-then-apply** | Preview of consequences before structural changes are committed. |
| **Command Palette** | `Cmd+K` unified entry point for actions, search, and navigation. |
| **Component Health** | Derived status for components — aggregated from children's statuses. |
## Key Architectural Decisions
### 1. Two Node Types: Components and Issues
**Decision:** The graph has two explicit types instead of untyped depth-as-type.
**Rationale:** Code repositories have inherent structure — repos, packages, modules, directories. If the system infers a skeleton from code, it needs to distinguish structural nodes (components) from work nodes (issues). A component at depth 3 is still a component. An issue at depth 1 is still an issue. Type is orthogonal to depth.
**Trade-off:** More rigidity than untyped model. Mitigated by keeping the type set minimal (just two) and allowing flexible nesting within each.
### 2. Two Separate Graphs
**Decision:** Decomposition tree and association graph are explicitly separated.
**Rationale:** Jira unifies parent/child and other link types, making neither clean. Separating them means the tree is always valid, reparenting is a distinct operation, and lateral links can be added/removed without structural impact.
### 3. Repository Integration as First-Class Feature
**Decision:** Multi-repo support with component↔repo linking and skeleton inference from day one.
**Rationale:** The cold-start problem kills structured tools. "Connect your code and we'll infer the skeleton" eliminates the biggest adoption barrier. It also makes code integration features (commit association, PR surfacing) dramatically better because the system knows which code maps to which component. This is the strongest differentiator vs. Linear/Jira.
### 4. Skeleton Inference (AI-Assisted Fast-Start)
**Decision:** When connecting repos, AI proposes a component tree from code structure, user adjusts and confirms.
**Rationale:** Manual component creation is slow and error-prone. Code structure is already a graph — package manifests, directory layout, module boundaries. Inferring the skeleton from code gives users a 90% starting point in seconds. The "adjust" step ensures human control.
### 5. Granular Policies from Day One
**Decision:** Full policy engine (actor + action + resource scope) in v0.1.
**Rationale:** The agent story requires fine-grained, resource-scoped permissions. Roles make it simple for users who don't need custom policies.
### 6. Resource Scope = Graph Position
**Decision:** Permissions scope to subtrees in the decomposition tree.
**Rationale:** The graph *is* the namespace. "Backend API subtree" = the component and everything under it. Permission management becomes visual.
### 7. Lateral Links: Visible but Opaque Across Boundaries
**Decision:** When an actor can see node A but not node B, a link A→B is visible (type shown) but B's details are withheld.
**Rationale:** Agents need to know about blockers even without full access.
### 8. Default Deny
**Decision:** If no policy explicitly allows an action, it is denied.
**Rationale:** Safer default for agents.
### 9. Agents as First-Class Actors
**Decision:** Agents have their own accounts with distinct identity in logs and UI.
**Rationale:** Traceability. "deploy-bot changed status" vs. "John (via script) changed status."
### 10. Keyboard-First Interaction
**Decision:** Command palette is primary navigation. Every action has a shortcut.
**Rationale:** Graph navigation has more dimensions than a flat list. Borrowed from Linear.
### 11. Plan-Then-Apply for Structural Changes
**Decision:** Reparenting, deletion, bulk operations show preview before committing.
**Rationale:** Structural changes cascade — permissions shift, links break, agents lose scope. Borrowed from Terraform.
### 12. Triage Inbox (Rootless Nodes)
**Decision:** Issues without a parent live in inbox. With repo integration, triage gets smart suggestions.
**Rationale:** Decouples "capture" from "organize." Smart triage uses code context to suggest the right component.
### 13. Dual Database (Neo4j + Postgres)
**Decision:** Neo4j owns graph topology. Postgres owns content and metadata. Linked by UUID.
**Rationale:** Cypher handles the graph queries Non-Linear needs. Clean separation of concerns.
### 14. `depends_on` Link Type
**Decision:** A new lateral link type specifically for inter-component dependencies.
**Rationale:** With repo integration, component dependencies can eventually be inferred from code (import graphs, API calls). This is a distinct semantic from issue-level "blocks" — it represents architectural coupling, not work sequencing.

View File

@ -1,76 +0,0 @@
# Non-Linear: Glossary & Key Decisions
## Glossary
| Term | Definition |
|------|-----------|
| **Node / Issue** | The atomic unit. A single item in the tracker. Untyped — its abstraction level is determined by tree depth. |
| **Decomposition Tree** | The strict parent→child hierarchy. The structural spine of a project. Every node has one parent (except roots). |
| **Lateral Link** | A typed edge between two nodes that doesn't affect hierarchy. Types: blocks, relates_to, duplicates. |
| **Layered Overview** | The primary graph view showing nodes arranged in rows by tree depth. |
| **Focus Widget** | The detail view showing one node with its parent and children. |
| **Actor** | Any entity that can perform actions: a human user or an AI agent. |
| **Policy** | A single permission rule: actor + action + resource scope + effect (allow/deny). |
| **Role** | A named, reusable bundle of policies. Convenience layer over the granular engine. |
| **Resource Scope** | Where a policy applies: global (whole project), subtree (node + descendants), or single node. |
| **Subtree** | A node and all of its descendants in the decomposition tree. |
| **Reparenting** | Moving a node to a different parent. A structural change that affects permissions. |
| **Opaque Link** | When a lateral link's target is outside the actor's permission scope. The link is visible but the target's details are withheld. |
| **Root Node** | The top-level node of a project's decomposition tree. Created automatically with the project. |
| **Depth** | A node's distance from the root in the decomposition tree. Root = 0, its children = 1, etc. |
## Key Architectural Decisions
### 1. Two Separate Graphs
**Decision:** The decomposition tree and the association graph are explicitly separated, not unified into a single "links with types" model.
**Rationale:** Jira unifies parent/child and other link types into one system, which makes neither clean. Separating them means:
- The tree is always a valid hierarchy (one parent per node, no cycles)
- Reparenting is a distinct, high-privilege operation
- The layered view derives directly from tree structure
- Lateral links can be freely added/removed without structural impact
### 2. Untyped Nodes (Depth = Type)
**Decision:** Issues have no explicit type field (epic, story, task). Tree depth implies abstraction level.
**Rationale:** Explicit types create rigidity. Teams argue about whether something is a "story" or a "task." Refactoring hierarchy means changing types manually. With depth-as-type, moving a node automatically changes its conceptual level. Labels handle all orthogonal classification.
**Trade-off:** The layered view can't use type-specific icons or colors by default. Solved by using depth-based visual styling (bolder at depth 0, lighter at depth 3+).
### 3. Granular Policies from Day One
**Decision:** Build the full policy engine (actor + action + resource scope) in v0.1, not bolt it on later.
**Rationale:** The agent story requires fine-grained, resource-scoped permissions. If we start with coarse RBAC and refine later, every integration and agent needs to be updated. Building granular-first means the agent API is permission-aware from the start. Roles (presets) make it simple for users who don't need custom policies.
**Trade-off:** More engineering effort upfront. Mitigated by shipping only 3 default presets with minimal custom-role UI.
### 4. Resource Scope = Graph Position
**Decision:** Permissions scope to subtrees in the decomposition tree, not to arbitrary resource groups.
**Rationale:** The graph *is* the namespace. "Backend API subtree" is a natural, intuitive boundary. No need for a separate resource-naming scheme like ARN paths. This makes permission management visual — you can see permission boundaries on the graph.
**Trade-off:** Reparenting has permission side effects. Requires warnings and potentially confirmation flows.
### 5. Lateral Links: Visible but Opaque Across Boundaries
**Decision:** When an actor can see node A but not node B, and there's a link A→B, the actor sees the link exists (and its type) but cannot read B's details.
**Rationale:** Agents need to know about blockers even if they can't access the blocking node's details. "Something in the Frontend subtree is blocking your task" is useful information. Hiding the link entirely would make agents unable to reason about cross-boundary dependencies.
**Trade-off:** The existence of a link reveals that a node exists in another subtree, which is a minor information leak. Acceptable for most team contexts.
### 6. Default Deny
**Decision:** If no policy explicitly allows an action, it is denied.
**Rationale:** Safer default, especially with agents. A misconfigured agent that's missing an allow policy does nothing (safe). A misconfigured agent in a default-allow system could do anything (dangerous).
### 7. Agents as First-Class Actors
**Decision:** Agents have their own accounts, not shared human credentials. They appear in assignee lists, comment histories, and audit logs with their own identity.
**Rationale:** Traceability. If an agent changes status on 50 nodes, the audit log shows "deploy-bot changed status" not "John (via script) changed status." This matters for trust and debugging.

View File

@ -0,0 +1,86 @@
# Non-Linear: Ideas Borrowed from Existing Products
## Overview
Design ideas mined from existing products, evaluated against Non-Linear's "focused dev tool" philosophy.
## ✅ Adopted for v0.1
### Keyboard-First + Command Palette (from Linear)
Every action has a keyboard shortcut. `Cmd+K` command palette is the primary navigation method. Graph navigation via `Alt+↑/↓/←/→`. Context-aware palette shows different actions depending on current view and selection.
### Triage Inbox / Rootless Inbox (from Linear)
Nodes without a parent land in a staging inbox. Triage = assigning a parent, which places the node in the tree. Decouples "capture" from "organize." Agents can triage via API.
### Backlinks / Inbound Link Surfacing (from Obsidian)
Every node surfaces inbound lateral links: "3 tasks blocked by this." Count badges on nodes in layered overview. Agents can query "what depends on this node?" for impact analysis.
### PR / Commit ↔ Node Coupling (from GitHub)
Short typeable node IDs (`NL-42`). Commit message parsing (`fixes NL-42` → status change). PR linkage appears in node's activity feed. Branch naming support (`feature/NL-42-login-flow`). GitHub/GitLab webhooks trigger node updates.
### Scoped Conversations + Selective Watching (from Slack)
Watch granularity: single node, subtree, or project-wide. Event type filters (status changes, comments, agent actions). Mute noisy subtrees. Agent-specific filter.
### Plan-Then-Apply (from Terraform/Pulumi)
Structural changes show preview: reparent consequences, bulk operation impact, deletion cascades, permission boundary changes. Agent proposals submitted as reviewable "plans."
### Cycles as Time-Boxing (from Linear)
Cross-cutting temporal view over spatial tree. Cycle = named set of node references + date range. Color highlighting in all views. Agent scoping to current cycle.
### Complete Change History / Blame Timeline (from Git)
Every mutation is an event: status, assignment, comment, link, reparent, label. Compact timeline in focus widget. Diff-style for structural changes. Agent-readable via API.
### Breadcrumb Navigation (from VS Code)
Full root→node path as clickable segments. Always visible in focus widget. Keyboard shortcut to jump to any ancestor.
### Natural Language Node Creation (from Todoist)
Parse tree path (`Auth > Login flow`), labels (`#bug`, `p0`), assignment (`@agent-1`) from text. Integrated into command palette. Fuzzy-matches node names. Falls back to inbox if path unresolvable.
## ⏳ Deferred
### Auto-Linking External Signals (from Sentry/PagerDuty) — v0.3+
Production errors auto-route to the right component subtree. Deferred because it can overwhelm UI and requires mature graph structure before automation.
## ❌ Not Adopting
### Rich Document Blocks (from Notion)
Turns dev tool into workspace platform. Nodes have rich markdown descriptions, not embedded apps.
### Multiplayer Presence / Cursors (from Figma)
Overkill for small teams (3-5). Ambient awareness handled by activity feed and scoped notifications.
### Canvas / Sketch-to-Structure (from Excalidraw)
Cool demo, unclear daily value. Entry experience (create root → add children) is fast enough.
## Summary Table
| Idea | Source | Status | Version |
|------|--------|--------|---------|
| Keyboard-first + Cmd+K | Linear | ✅ Adopted | v0.1 |
| Triage / rootless inbox | Linear | ✅ Adopted | v0.1 |
| Backlinks | Obsidian | ✅ Adopted | v0.1 |
| PR/commit coupling | GitHub | ✅ Adopted | v0.1 |
| Scoped conversations + watching | Slack | ✅ Adopted | v0.1 |
| Plan-then-apply | Terraform | ✅ Adopted | v0.1 |
| Cycles (color-highlighted) | Linear | ✅ Adopted | v0.1 |
| Complete change history | Git | ✅ Adopted | v0.1 |
| Breadcrumb navigation | VS Code | ✅ Adopted | v0.1 |
| Natural language creation | Todoist | ✅ Adopted | v0.1 |
| Auto-link external signals | Sentry/PagerDuty | ⏳ Deferred | v0.3+ |
| Rich document blocks | Notion | ❌ Rejected | — |
| Multiplayer presence | Figma | ❌ Rejected | — |
| Canvas sketch-to-structure | Excalidraw | ❌ Rejected | — |

View File

@ -0,0 +1,217 @@
# Non-Linear: Tech Stack & Architecture
## Stack Overview
```
┌─────────────────────────────────────────────────────────┐
│ FRONTEND │
│ Vue 3 + Tailwind + Headless UI + ECharts │
│ Graph Viz: TBD (D3 vs Cytoscape — eval pending) │
│ Command Palette: vue-command-palette / custom │
│ Keybindings: VueUse useMagicKeys │
│ Icons: Lucide │ Font: Inter │ Motion: @vueuse/motion
│ State: Pinia │ HTTP: ofetch │ WS: native/socket.io │
├─────────────────────────────────────────────────────────┤
│ CROSS-PLATFORM │
│ Desktop: Tauri (wraps Vue app) — v0.1 │
│ Mobile: Capacitor (responsive web first) — v0.2+ │
├─────────────────────────────────────────────────────────┤
│ BACKEND │
│ FastAPI (Python) │
│ Taskiq (async task queue — webhooks, imports, agents) │
├─────────────────────────────────────────────────────────┤
│ DATA LAYER │
│ Neo4j — issue graph (nodes, edges, status, labels) │
│ Postgres — content & metadata (rich text, comments, │
│ attachments meta, audit logs, project cfg) │
│ Redis — caching, WebSocket pub/sub, rate limiting │
│ Meilisearch — full-text search (issues, comments) │
│ MinIO — S3-compatible file storage (attachments) │
├─────────────────────────────────────────────────────────┤
│ AUTH │
│ Authentik — OIDC, API tokens, role mgmt, SSO-ready │
├─────────────────────────────────────────────────────────┤
│ DEPLOYMENT │
│ Docker Compose (dev + self-hosted) │
└─────────────────────────────────────────────────────────┘
```
## Data Boundary
### Neo4j — Graph Topology
Owns the decomposition tree and lateral links:
- Node identity (UUID), short ID
- Lightweight properties: status, labels, assignee_id, created_at, updated_at
- Parent → child edges (decomposition tree)
- Lateral link edges: blocks, blocked_by, relates_to, duplicates
- Project root references, cycle membership
**Why Neo4j over Postgres recursive CTEs:** Queries like "find all unblocked leaves in this subtree," "critical path through blocks links," "everything 3 hops from this node" are what Cypher is built for. CTEs get painful with lateral links and variable-depth queries. The gap widens in v0.2+ with cross-project edges.
### Postgres — Content & Metadata
- **Rich text content:** issue descriptions (markdown)
- **Comment threads:** body, author, parent_comment_id (threading), timestamps
- **Attachment metadata:** filename, size, mime_type, s3_key, uploader_id, uploaded_at
- **User/agent accounts:** profile data, preferences, notification settings
- **Project settings:** configuration, member lists, default policies
- **Audit logs:** who changed what, when, with before/after snapshots
- **Policy definitions:** role templates, custom permission rules
**Linked to Neo4j by UUID.** Neo4j node stores `id: "abc-123"`. Postgres stores full content keyed by same UUID. FastAPI joins them as needed.
### Redis — Caching & Real-Time
- Subtree query cache (TTL, invalidated on graph mutations)
- WebSocket pub/sub for real-time updates
- Rate limiting for agent API
- Authentik token validation cache
### Meilisearch — Search Index
- Indexes issue titles, descriptions, comments, labels
- Fed from both Neo4j and Postgres
- Powers command palette search (issues + commands in one result set)
- Typo-tolerant, prefix search, filtering by label/status/assignee
### MinIO — File Storage
- S3-compatible API, self-hosted
- Stores attachment files (images, docs)
- Postgres stores metadata and S3 key; MinIO stores bytes
- Migration path to AWS S3: zero code changes
## Backend Architecture
### FastAPI Application Structure
```
non-linear-api/
├── app/
│ ├── main.py # App, middleware, startup/shutdown
│ ├── config.py # Settings from env vars
│ ├── dependencies.py # Shared deps (db sessions, auth, current_user)
│ ├── auth/ # Authentik integration
│ │ ├── oidc.py # Token validation, OIDC discovery
│ │ ├── permissions.py # Policy engine evaluation
│ │ └── agent_tokens.py # API token management for agents
│ ├── graph/ # Neo4j layer
│ │ ├── connection.py # Neo4j driver management
│ │ ├── queries.py # Cypher query templates
│ │ ├── mutations.py # Graph write operations
│ │ └── traversal.py # Subtree, path, neighbor queries
│ ├── content/ # Postgres layer
│ │ ├── models.py # SQLAlchemy/SQLModel models
│ │ ├── descriptions.py # Rich text CRUD
│ │ ├── comments.py # Comment thread CRUD
│ │ └── attachments.py # Metadata + MinIO upload/download
│ ├── search/ # Meilisearch integration
│ │ ├── indexer.py # Index updates on mutations
│ │ └── search.py # Query interface
│ ├── realtime/ # WebSocket layer
│ │ ├── manager.py # Connection management
│ │ └── events.py # Event types and broadcasting
│ ├── tasks/ # Taskiq background jobs
│ │ ├── webhooks.py # Deliver webhooks to agent endpoints
│ │ ├── indexing.py # Async search index updates
│ │ └── notifications.py # Notification delivery
│ └── api/v1/ # Route handlers
│ ├── nodes.py # CRUD + tree operations
│ ├── links.py # Lateral link management
│ ├── projects.py # Project CRUD
│ ├── comments.py # Comment endpoints
│ ├── attachments.py # Upload/download
│ ├── search.py # Search endpoint
│ └── agent.py # Agent-specific API surface
├── tests/
├── alembic/ # Postgres migrations
├── docker-compose.yml
└── pyproject.toml
```
### Request Flows
**Typical read ("get node with full context"):**
```
Client → FastAPI → Auth middleware (validate token via Authentik)
→ Policy engine (check permissions)
→ Neo4j: fetch node + parent + children + links
→ Postgres: fetch description, comments, attachment meta
→ Merge response → Client
```
**Typical write ("change node status"):**
```
Client → FastAPI → Auth → Policy engine
→ Neo4j: update node status
→ Redis: invalidate cache, publish event
→ Taskiq: queue webhook delivery, search index update
→ WebSocket: broadcast to connected clients
→ Response → Client
```
### Sync Strategy (Neo4j ↔ Postgres)
Not replicated — they own different data. Linked by UUID. Both operations happen in same API request. Compensating transaction pattern for consistency. Eventual consistency acceptable for search index and cache.
## Auth Architecture
```
┌──────────┐ OIDC token ┌───────────┐
│ Vue App ├─────────────────────►│ Authentik │
└────┬─────┘ (login flow) └─────┬─────┘
│ │
│ Bearer token │ Token introspection
▼ ▼
┌──────────┐◄────────────────────┌───────────┐
│ FastAPI │ validate token │ Authentik │
│ (resource│ check claims │ (OIDC │
│ server) │ │ provider)│
└──────────┘ └───────────┘
```
- **Human users:** OIDC login flow. JWT access tokens.
- **AI agents:** API tokens issued through Authentik, tied to agent actor accounts.
- **FastAPI:** pure resource server. Validates tokens, reads claims, enforces policies.
## Design Language
Targets Linear's aesthetic: minimal, fast, slightly dark-IDE feel.
- **Spacing:** tight, no wasted space
- **Colors:** muted base palette, high-contrast accents only for status/priority
- **Borders:** almost none — separation via spacing and subtle background shifts
- **Dark mode:** default, light mode secondary
- **Typography:** Inter, small-but-readable sizes
- **Animations:** subtle slides and fades, 100-150ms, nothing bouncy
- **Optimistic updates:** every interaction feels instant, syncs in background
## Docker Compose (Dev)
```yaml
services:
api: # FastAPI
frontend: # Vue 3 (vite dev / nginx prod)
worker: # Taskiq worker (same codebase as api)
neo4j: # Graph database
postgres: # Relational database
redis: # Cache + pub/sub
meilisearch: # Search engine
minio: # Object storage
authentik: # Identity provider (server + worker)
authentik-db: # Authentik's own Postgres
```
~10 containers. Runs comfortably on 16GB RAM.
## Open Technical Questions
1. **Graph viz library:** D3 vs Cytoscape — prototype comparison pending
2. **Real-time strategy:** WebSocket via FastAPI or dedicated service (Centrifugo)?
3. **Neo4j driver:** official `neo4j` Python driver vs `neomodel` OGM
4. **Rich text format:** Markdown (simple) vs ProseMirror JSON (collaborative editing)
5. **Gantt implementation:** custom or frappe-gantt as starting point