Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions engine/app/services/coplan/plan_types/install_defaults.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
module CoPlan
module PlanTypes
# Installs the plan types shipped with the engine
# (engine/db/default_plan_types/*.md — YAML front matter for the
# attributes, Markdown body as the template).
#
# Admin edits are data, defaults are code, and the defaults must never
# silently clobber the data: without `force`, existing types only gain
# values for fields that are currently blank (the common upgrade case —
# a type created before templates existed gets the default template,
# but a hand-written description survives). With `force`, the shipped
# defaults win on every field — blank shipped values included, so a
# custom template on a type that ships without one (Scratchpad, General)
# is cleared. Types unknown to the defaults are never touched either way.
class InstallDefaults
DEFAULTS_DIR = CoPlan::Engine.root.join("db", "default_plan_types")
FRONT_MATTER = /\A---\n(?<yaml>.*?)\n---\n?(?<body>.*)\z/m

Result = Struct.new(:created, :updated, :skipped, keyword_init: true)

def self.call(force: false, dir: DEFAULTS_DIR)
new(force:, dir:).call
end

def initialize(force: false, dir: DEFAULTS_DIR)
@force = force
@dir = Pathname(dir)
end

def call
result = Result.new(created: [], updated: [], skipped: [])

@dir.glob("*.md").sort.each do |path|
attrs = parse(path)
type = PlanType.find_by_name(attrs[:name])

if type.nil?
PlanType.create!(**attrs)
result.created << attrs[:name]
elsif apply(type, attrs)
result.updated << attrs[:name]
else
result.skipped << attrs[:name]
end
end

result
end

private

def parse(path)
match = FRONT_MATTER.match(path.read)
raise ArgumentError, "#{path.basename}: missing YAML front matter" unless match

meta = YAML.safe_load(match[:yaml]) || {}
name = meta["name"].to_s.strip
raise ArgumentError, "#{path.basename}: front matter needs a name" if name.empty?

{
name: name,
description: meta["description"].to_s.strip.presence,
icon: meta["icon"].to_s.strip.presence,
default_tags: Array(meta["default_tags"]).map(&:to_s),
template_content: match[:body].strip.presence
}
end

# Assigns default values onto an existing type; returns whether
# anything changed. Only blank fields are filled unless forcing;
# forcing restores the shipped value even when it's blank.
def apply(type, attrs)
attrs.except(:name).each do |field, value|
next if value.blank? && !@force
next unless @force || type[field].blank?

type[field] = value
end
return false unless type.changed?

type.save!
true
end
end
end
end
52 changes: 52 additions & 0 deletions engine/db/default_plan_types/engineering-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
name: Engineering Design
icon: scroll
description: >-
A formal record of a technical design decision, written for reviewers
deciding whether to build it and maintainers later asking why it was.
Alternatives weighed, risks named. Still exploring options? Use
Exploration instead.
---
<!-- Engineering Design: a decision record. The reader is a reviewer with
five minutes, then a maintainer two years from now. Keep the whole
document readable in five minutes. Delete these comments as you fill
each section in. -->

## Problem

<!-- 2-3 sentences. What breaks, or stays broken, if nothing is done.
State it plainly - no selling. -->

## Constraints

<!-- The non-negotiables: compatibility requirements, deadlines, systems
that must not change, budgets. These justify the design below. -->

## Design

<!-- What will be built. Lead with a mermaid diagram when structure or
flow explains it faster than prose. Be concrete: component names,
boundaries, data shapes, failure behavior. -->

## Alternatives considered

<!-- One row per real alternative, including "do nothing". A design
without alternatives reads as a decision that was never examined.

| Option | Why not |
|--------|---------|
-->

## Risks

<!-- What could go wrong with the chosen design, and how you would
notice it happening. -->

## Rollout

<!-- How it ships safely: order of changes, flags, data migration, and
the way back if it goes wrong. -->

## Open questions

<!-- Decisions deliberately not made yet, and what resolves each one. -->
29 changes: 29 additions & 0 deletions engine/db/default_plan_types/exploration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
name: Exploration
icon: compass
description: >-
Working through a problem that is not decided yet - candidate
approaches, sketches, code samples, tradeoffs. The reader is you, your
collaborators, and the agent working alongside you. When one approach
wins, retype this as an Engineering Design and restructure.
---
<!-- Exploration: thinking in progress, shared. Structure is loose on
purpose - fragments and code samples are welcome. Keep dead ends in the
document: they save the next reader from redigging the same hole. -->

## Question

<!-- One line: what are we trying to figure out? -->

## Approaches

<!-- One ### subsection per approach. Sketch it, code-sample it, note
what it costs and what it buys. -->

## Current leaning

<!-- Which way you are leaning, and why. -->

## What would change my mind

<!-- The facts, measurements, or results that would flip the leaning. -->
7 changes: 7 additions & 0 deletions engine/db/default_plan_types/general.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
name: General
icon: file-text
description: >-
A plan that fits no other type. Check the type list before choosing
this - a more specific type almost always exists.
---
38 changes: 38 additions & 0 deletions engine/db/default_plan_types/handoff.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
name: Handoff
icon: file-text
description: >-
State transfer at the end of a work session - agent or human - for
whoever picks the work up next. Optimize their first ten minutes.
Archive it once it has been picked up.
---
<!-- Handoff: written for whoever continues this work, possibly with
none of your context. Links beat prose - every claim of "done" carries
its artifact. -->

## Goal of the work

<!-- What the overall effort is trying to achieve, in a line or two. -->

## Done

<!-- What is complete, each item with its artifact link: PR, commit,
plan, document. -->

## Not done

<!-- What remains. Be honest - this list is why the handoff exists. -->

## Decisions made

<!-- Choices settled during the session and the reasoning, so the next
person doesn't relitigate them. -->

## Landmines

<!-- Gotchas discovered the hard way: flaky tests, misleading names,
things that look broken but aren't. -->

## Next steps

<!-- Where to start, in order. The first item is the very next action. -->
37 changes: 37 additions & 0 deletions engine/db/default_plan_types/implementation-plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
name: Implementation Plan
icon: map
description: >-
A step-by-step plan for building a specific change - the document an
agent or engineer executes. Steps are checkboxes with a verification
each. The reader is whoever does the work, and whoever approves it
first.
---
<!-- Implementation Plan: a living checklist. Check steps off as you
execute - the checkboxes are the plan's state, so no separate status
notes. Each step needs a way to verify it worked. -->

## Goal

<!-- The end state, in 1-2 sentences. -->

## Current state

<!-- What exists now, with the file and repo references the executor
starts from. -->

## Steps

<!-- Each step: a concrete action and how to verify it worked. Split
any step you cannot verify. -->

- [ ] First step — verify: …
- [ ] Second step — verify: …

## Risks & rollback

<!-- What might break while executing, and the way back if it does. -->

## Out of scope

<!-- Nearby work this plan deliberately does not touch. -->
35 changes: 35 additions & 0 deletions engine/db/default_plan_types/prd.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
name: PRD
icon: scale
description: >-
A product requirements document: what to build and why, for the team
building it and the stakeholders agreeing to it. The how belongs in an
Engineering Design.
---
<!-- PRD: the agreement about what gets built. Requirements must be
testable statements - if you cannot check it, it is not a requirement. -->

## Problem

<!-- Who has the problem, when it bites them, and the evidence it is
real. -->

## Goals

<!-- What done looks like, as outcomes - not a feature list. -->

## Non-goals

<!-- What this deliberately does not do. As load-bearing as the goals:
scope disputes get settled here. -->

## Requirements

<!-- Numbered, each marked Must or Should, each testable. -->

## Success metrics

<!-- How you will know it worked: measurable, with the current baseline
when known. -->

## Open questions
36 changes: 36 additions & 0 deletions engine/db/default_plan_types/project-1-pager.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
name: Project 1-Pager
icon: rocket
description: >-
A one-page pitch for a project: the problem, the bet, what it costs,
what changes if it works. The reader has ten minutes and decides
whether this deserves investment. Persuasive language is welcome in
this type - but every claim still needs a specific behind it.
---
<!-- Project 1-Pager: the whole case on one page. This type overrides
the default writing-style rules: persuasion is allowed. Specifics are
still required - an adjective is not evidence. If it runs past a page,
cut until it fits. -->

## The problem

<!-- What hurts today, for whom, and what it costs to leave alone. -->

## The bet

<!-- What we would do, and the outcome we believe it produces. -->

## What it takes

<!-- People, time, dependencies. Honest costs - a pitch that hides the
bill gets one meeting. -->

## What changes if it works

<!-- The after-state, concretely. Numbers where you have them. -->

## Why now

<!-- What makes this the right moment rather than next quarter. -->

## Open questions
38 changes: 38 additions & 0 deletions engine/db/default_plan_types/research.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
name: Research
icon: flask
description: >-
Findings from an information-gathering run - internal systems,
history, competitive analysis, legal, external sources. Every claim
carries a footnote citation to a durable source and the date it was
confirmed. The reader acts on the findings without redoing the work.
---
<!-- Research: the reader trusts this document instead of re-searching.
That trust is built one citation at a time - every claim gets a
footnote[^like-this] with a durable source link and the date you
confirmed it. Dates on facts are required here; dates on the document
are still banned. -->

## Question

<!-- What this research set out to answer. -->

## Answer

<!-- The findings up front, in a few sentences: your best supported
answer and how confident you are. Not "it depends". -->

## Findings

<!-- One ### subsection per finding. Cite every claim. Distinguish what
a source says from what you infer. -->

## What we still don't know

<!-- The gaps and unconfirmed claims, and what it would take to close
each one. -->

## Method

<!-- Where you looked, briefly - enough for someone to extend the
search, not a diary of it. -->
8 changes: 8 additions & 0 deletions engine/db/default_plan_types/scratchpad.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
name: Scratchpad
icon: lightbulb
description: >-
A brainstorming space with no structure required - not expected to be
readable by anyone else yet. When it firms up, retype it (usually as
an Exploration or Engineering Design) and restructure.
---
Loading
Loading