TRD — Impact Model Specification — Go-first model, JSON Schema, measurement, and leadership reporting¶
Note on names. This specification was written when the product was called the Theory of Change specification. It is now the impact model, and the theory of change, the logic model, the outcome map, and the logframe are projections of it. Below, "theory" means the impact model document unless it names the method. See the decision.
Initiative: INIT-THEORYOFCHANGESPEC-001
Repository: github.com/ProductBuildersHQ/impact-model
Version: 0.1
Status: Draft
This TRD is a technical contract: what the module MUST guarantee. MUST / MUST
NOT / SHOULD / MAY are used in the RFC 2119 sense. Conditions that do not
apply are answered Not applicable — <rationale>.
1. Introduction¶
1.1 Purpose¶
Define the Go module github.com/ProductBuildersHQ/impact-model: a
Go-struct-first Theory of Change (ToC) model, its generated JSON Schemas, and
the library, CLI, and MCP tooling that validate, analyze, measure, judge, and
report on ToC documents. This TRD lets implementation proceed phase by phase
(PLAN.md) without inventing product decisions.
1.2 Scope¶
In scope: data model, schema pipeline, validation, causal-graph analysis, deterministic lint, measurement roll-up, report rendering, spec template and LLM-judge rubric, MCP server, PRISM adapters, and the USM/UFM integration contract.
Out of scope: see PRD §2.2 (no USM/UFM implementation, no UI, no LLM calls, no persistence, no statistical inference).
1.3 References¶
| Document | Link |
|---|---|
| PRD | PRD.md |
| PLAN | PLAN.md |
| ROADMAP | ROADMAP.md |
| Research brief | docs/research/theory-of-change.md |
| prism-core | github.com/grokify/prism-core (Person, Metadata, Risk, health, trend, CapabilityRef) |
| prism-roadmap | github.com/grokify/prism-roadmap (OKR, OutcomeJourney) |
| structured-evaluation | github.com/plexusone/structured-evaluation (RubricSet, SummaryReport) |
| market-spec | github.com/ProductBuildersHQ/market-spec (EntityRef convention, actor/market nouns) |
2. Technical Requirements and Traceability¶
| TRD ID | Requirement | Source | Verification |
|---|---|---|---|
| TRD-001 | pkg/impact MUST define Model and all element types in §7 with camelCase JSON tags |
PRD-FR-101…110 | Round-trip marshal tests; schema lint |
| TRD-002 | pkg/impact MUST define ObservationSet as a separate document type |
PRD-FR-111 | Round-trip test; schema test |
| TRD-003 | cmd/generate-schema MUST be the only producer of schema/*.schema.json; CI MUST fail if regenerating changes any file |
PRD-FR-201 | go generate ./schema && git diff --exit-code schema/ |
| TRD-004 | schema package MUST embed every generated schema and expose Get(name) and Names() |
PRD-FR-202 | schema_test.go loads every name |
| TRD-005 | pkg/impactio MUST load .json, .yaml, .yml by converting YAML → JSON before decoding and validating |
PRD-FR-203 | Same fixture in both formats yields equal Model and equal issues |
| TRD-006 | pkg/validate MUST report schema, uniqueness, ID-format, and reference errors with JSON-pointer paths |
PRD-FR-301 | Table-driven tests per error class |
| TRD-007 | pkg/graph MUST implement the queries in §6.2 in O(V+E) per query (paths enumeration excepted, bounded by MaxPaths) |
PRD-FR-302 | Unit tests; benchmark on 1,000-node graph |
| TRD-008 | pkg/lint MUST implement the rules in §6.4 with stable IDs and emit a structured-evaluation summary.SummaryReport |
PRD-FR-303, 304 | Rule registry test; seeded-defect golden test |
| TRD-009 | pkg/measure MUST implement §6.5 algorithms deterministically for a given asOf |
PRD-FR-401…406 | Table tests; golden status snapshots |
| TRD-010 | pkg/report MUST render the standard reports in §6.6 and accept user templates over report.View |
PRD-FR-501, 502, 504 | Golden-file tests, run twice |
| TRD-011 | Renderers MUST NOT emit restricted evidence excerpts |
PRD-FR-503 | Test with restricted evidence asserts absence |
| TRD-012 | spec/ MUST embed toc.md template and toc.rubric.yaml that parses into rubric.RubricSet and passes its Validate() |
PRD-FR-601 | spec_test.go |
| TRD-013 | pkg/mcp MUST expose the six tools in §6.3 over stdio with the official Go SDK |
PRD-FR-602 | In-memory transport integration test |
| TRD-014 | pkg/prismx MUST export OKR documents, outcome journeys, V2MOM documents (one method per pathway; refused without a vision, values, or a pathway), and DMAIC metrics documents (one category per pathway marked dmaic; control limits only from ≥ 20 observations; refused when nothing is marked or nothing is numeric); no package under pkg/impact, pkg/graph, pkg/validate, pkg/lint, pkg/measure MAY import prism-roadmap |
PRD-FR-603 | go list -deps import-boundary test |
| TRD-015 | pkg/ref MUST parse and format the reference grammar in §6.7 |
PRD-FR-604 | Parse/format round-trip and invalid-input tests |
| TRD-016 | Core types MUST reuse prism-core Person, Risk, CapabilityRef, health and trend constants instead of redefining them |
PRD G-7 | Code review; grep test for duplicate type names |
| TRD-017 | pkg/impact MUST define Pathway, Metric, Audience, GlossaryTerm, Theme, SelectionCriterion, MeasurementBudget, and the indicator extensions in §7.1 |
PRD-FR-112…117 | Round-trip tests; schema lint |
| TRD-018 | ObservationSet MUST carry stories, learningReviews, and per-observation quality (§7.2) |
PRD-FR-118 | Round-trip tests; validation of theme and consent rules |
| TRD-019 | pkg/measure MUST compute metric roll-ups across pathways per §6.5 and return baseline_only for indicators without targets |
PRD-FR-113, 509 | Table tests |
| TRD-020 | pkg/burden MUST total collection cost per period and per respondent and compare against MeasurementBudget |
PRD-FR-115 | Unit tests; collection-plan golden |
| TRD-021 | pkg/tabular MUST import observations from CSV via a mapping file and export definition tables as CSV (RFC 4180) |
PRD-FR-204 | Import tests with seeded bad rows; export golden files |
| TRD-022 | pkg/report MUST render the definition reports with no observations and the story, learning-review, and portfolio reports in §6.6 |
PRD-FR-505…508 | Golden-file tests |
| TRD-023 | Story quotes MUST be rendered only when consent is granted and sensitivity is renderable |
PRD-FR-506 | Restricted / no-consent absence tests |
| TRD-024 | skills/ MUST contain the agent skills in §6.11; each skill's command sequence MUST be executable by a smoke test |
PRD-FR-605 | Skill smoke test runs commands twice and compares outputs |
| TRD-025 | pkg/impact MUST define Cause, Effect, Consultation, TargetGroup, outcome responsibility / deliveredBy / addressesCauses / addressesEffects, priority scores, and the assumption and risk fields in §7.1 |
PRD-FR-105, 108, 119…123, 126 | Round-trip tests; schema lint |
| TRD-026 | Measurement MUST monitor every outcome regardless of responsibility, and reports MUST separate accountable (own, shared) from monitored-only (partner, unaddressed) outcomes |
PRD-FR-120 | Status and report tests over mixed-responsibility fixture |
| TRD-027 | pkg/report MUST render the narrative and actors reports in §6.6 |
PRD-FR-510, 511 | Golden files |
| TRD-028 | spec/ MUST ship the core rubric plus a development rubric profile covering the UNDG checklist split in research §9.6 |
PRD-FR-601 | Both parse and Validate(); checklist-coverage test maps every checklist item to a criterion ID |
| TRD-029 | pkg/edit MUST implement the patch engine in §6.12 (RFC 6902 + ID addressing, atomic apply, error-delta rejection) and be the only mutation path used by CLI, SDK helpers, and MCP |
PRD-FR-701, 702, 706 | Engine tests; grep test that cmd/ and pkg/mcp never mutate impact.Model directly |
| TRD-030 | Element commands MUST compile to patches; rm MUST report dangling references and honor --cascade |
PRD-FR-703 | Command ↔ patch equivalence tests; reference tests |
| TRD-031 | pkg/impactio MUST provide canonical serialization (Format) for JSON and YAML; idempotent |
PRD-FR-704 | Fixture test runs twice |
| TRD-032 | pkg/diff MUST compute an ID-keyed semantic diff and draft a Revision |
PRD-FR-705 | Golden diffs |
| TRD-033 | pkg/slides MUST render Marp decks in §6.13 from report.View; export runs only in cmd/impact-model via external CLIs |
PRD-FR-801, 802; INV-7 | Golden decks; export test skipped when CLIs absent |
| TRD-034 | pkg/judge MUST build judge bundles, validate verdicts as structured-evaluation reports, and aggregate with agreement metrics (§6.14) |
PRD-FR-803 | Bundle golden; verdict validation tests; aggregation tests |
| TRD-035 | pkg/lint MUST support draft, standard, full profiles; the minimal fixture is GO under draft |
PRD-FR-804 | Profile tests |
| TRD-036 | Every element type MUST embed Provenance (§7.1); indicators carry dataAvailability; rejected elements are excluded from graph, measurement, and default reports |
PRD-FR-124, 125 | Round-trip; exclusion tests |
| TRD-037 | impact-model evidence add and the impact-draft-from-sources skill per §6.11 |
PRD-FR-606, US-19 | Skill smoke test over saved fixture pages |
| TRD-038 | assessment report per §6.6 |
PRD-FR-512 | Golden file |
| TRD-039 | pkg/share MUST produce the pandoc Markdown profile, review packet, review sheet, and export stamps (§6.15) |
PRD-FR-901…903 | Golden files; stamp tests per format |
| TRD-040 | pkg/review MUST validate ReviewResponse and produce a proposed patch plus Consultation without modifying the document (§6.15) |
PRD-FR-904, 905 | Import tests incl. stale hash |
| TRD-041 | impact-model approve per §6.15 is the only writer of status approved |
PRD-FR-707, US-21 | Approval tests; grep test |
| TRD-042 | skills/toc-share-google per §6.11, with manual fallback |
PRD-FR-607 | Skill smoke test with recorded tool outputs |
| TRD-043 | pkg/problemtree MUST build the problem tree as layered nodes and edges, export it as a d2vision diagram spec (JSON), and render D2 and Mermaid from that spec; the problem-tree, problem-tree-d2, and problem-tree-json reports per §6.6 |
PRD-FR-514 | Golden diagram tests on the examples; layering and cycle tests |
3. System Invariants¶
- INV-1 Every element ID MUST be unique within a document across all element kinds (outcomes, links, interventions, assumptions, indicators, actors, evidence, factors, risks).
- INV-2 No library function MAY mutate its input
ModelorObservationSet; derived data is returned in new values. - INV-3 For identical inputs (documents, observation sets,
asOf, options), every computed output (issues, status snapshot, report bytes) MUST be byte-identical. Map iteration MUST NOT influence output order; ordering is by document order, then ID. - INV-4 Unknown references MUST be reported, never silently dropped.
- INV-5 A score shown to a reader MUST be accompanied by its inputs or a link to them (indicator observations, assumption assessments, evidence IDs).
- INV-6
restrictedevidence (and evidence with no sensitivity set) MUST NOT be rendered in any output intended for humans (Markdown, HTML, Mermaid labels). - INV-7 The library MUST NOT perform network I/O or execute external
programs. Only
cmd/andpkg/mcpread or write files, and only paths given by the caller; onlycmd/impact-modelmay invoke the optional external export tools (marp,mmdc,pandoc). The module has no Google or other cloud API dependency. - INV-10 Review imports never modify a document; they produce proposals
that are applied only through
pkg/edit(INV-9). - INV-8 Schema files MUST only change via
cmd/generate-schema. - INV-9 Every change to a ToC document made by this module goes through
pkg/edit; no other code path writes a modified document.
4. Architecture¶
4.1 High-Level Architecture¶
┌──────────────── authoring ────────────────┐
human ──► impact-model.yaml / impact-model.json observations-*.yaml ◄── analyst
agent ──► (via MCP tools) │
└───────────────┬──────────────┘
▼
pkg/impactio load (YAML→JSON), decode, schema-validate (schema/ embed)
│
┌─────────────────┼──────────────────────────┐
▼ ▼ ▼
pkg/validate pkg/graph ◄──────────── pkg/lint (rules use graph)
(refs, IDs) (paths, cycles, → structured-evaluation
levels, ceiling) SummaryReport (GO/NO-GO)
│
▼
pkg/measure (indicator → outcome → pathway,
│ assumption propagation, confidence)
▼
impact.Status snapshot (JSON, schema-defined)
│
┌─────────────┼──────────────┐
▼ ▼ ▼
pkg/report pkg/prismx spec/ (template + RubricSet
(MD, Mermaid, (OKR, outcome for LLM-as-a-Judge, run by
tables, user journey export) visionspec / visionstudio)
templates)
▲
thin adapters: cmd/impact-model (Cobra CLI) and pkg/mcp (MCP stdio server)
both call one service layer: pkg/service
4.2 Component Responsibilities¶
| Component | Purpose | Responsibilities |
|---|---|---|
pkg/impact |
Data model | Go structs, enums with Valid()/All…() helpers, small accessors (OutcomeByID, IndicatorsFor). No I/O. |
pkg/ref |
References | Grammar in §6.7: parse, format, validate slugs. |
schema/ |
Schemas | //go:embed *.schema.json; Get, Names, Validate(name, []byte) using santhosh-tekuri/jsonschema/v6. |
cmd/generate-schema |
Schema pipeline | Reflect Go types with invopop/jsonschema; write files; deterministic output. |
pkg/impactio |
Load / save | File and byte loading for JSON/YAML; merge multiple observation sets; pretty JSON writer. |
pkg/validate |
Structural validation | Schema + INV-1 + reference resolution + enum validity → []Issue. |
pkg/graph |
Causal graph | Build from links + intervention targets; queries in §6.2. |
pkg/lint |
Quality rules | Rule registry, §6.4 rules, options (disable / severity override), SummaryReport output. |
pkg/measure |
Measurement | §6.5 algorithms; produces impact.Status; Diff(prev, cur). |
pkg/report |
Rendering | View model, standard templates (embedded), Mermaid builder, user templates. |
pkg/prismx |
PRISM adapters | ToOKR, ToOutcomeJourneys, ToV2MOM, ToDMAIC; prism-roadmap import is confined here. |
pkg/service |
Service layer | One function per user operation (Validate, Lint, Graph, Status, Report, Export, Schema) used by CLI and MCP. |
pkg/mcp |
MCP server | Tool registration and JSON I/O over pkg/service. |
spec/ |
Spec content | toc.md template, toc.rubric.yaml, judge instructions; embedded. |
cmd/impact-model |
CLI | Cobra commands in §6.1; no business logic. |
pkg/burden |
Collection burden | Staff-minute and respondent-question totals per period, pathway, and mode; budget comparison. |
pkg/tabular |
Spreadsheet interop | CSV observation import via mapping; CSV export of definition tables. |
skills/ |
Agent skills | SKILL.md instruction files plus templates; every computation delegated to impact-model CLI / MCP tools. |
pkg/edit |
Mutations | Patch engine (RFC 6902 + ID addressing), element-command builders, reference cascade. The only way documents change. |
pkg/diff |
Theory diff | ID-keyed semantic diff between two documents; draft Revision. |
pkg/slides |
Presentations | Marp Markdown decks from report.View; Mermaid sources embedded or referenced as SVG paths. |
pkg/share |
Sharing exports | pandoc Markdown profile, review packet, review sheet, export stamps; DOCX/PPTX conversion invoked only by cmd/impact-model. |
pkg/review |
Review import | ReviewResponse validation, mapping rows to proposed patch ops and flagged notes, Consultation record. |
pkg/judge |
LLM-as-a-Judge I/O | Judge bundle assembly, verdict validation (structured-evaluation), multi-judge aggregation and agreement. No model calls. |
examples/ |
Reference ToCs | Intermediary (multi-pathway, two-layer) example, market-adoption example, competing-futures fixture. |
4.3 Data Flow¶
- Load:
impactio.LoadDocument(path)→ bytes → (YAML→JSON) → schema validate →json.Unmarshalintoimpact.Model. - Validate:
validate.Model(doc)→ issues. Lint and measure refuse to run on documents witherror-severity validation issues (they returnErrInvalidDocumentwrapping the issues). - Graph:
graph.Build(doc); reused by lint, measure, report. - Measure:
measure.Evaluate(doc, g, obsSets, opts)→impact.Status. - Report:
report.Build(doc, g, status, opts)→report.View→ template → bytes.
5. State and Lifecycle¶
Applicability: Applicable.
| Entity | States | Trigger | Invalid Transitions |
|---|---|---|---|
Document (metadata.status) |
draft → in_review → approved → active → archived (prism-core status constants) |
Author edits field | None enforced by library (git is the audit trail); lint warns when active documents have open error findings |
| Intervention | planned, in_progress, completed, paused, cancelled |
Author edits | None enforced |
| Assumption | untested, holding, at_risk, invalidated, validated |
Latest AssumptionAssessment in observation sets overrides the document's static status |
None enforced; history kept in observation sets |
| Indicator status | Computed: achieved, on_track, at_risk, off_track, not_started, no_data, stale, baseline_only |
measure.Evaluate |
Not stored by authors |
| Indicator selection | candidate → selected → retired (and candidate → retired) |
Author edits | retired → selected requires a Revision entry (lint info) |
- Idempotency: All operations are pure functions of their inputs; re-running is safe.
- Concurrency: Not applicable — no shared mutable state; files are owned by git.
- Retention / deletion: Not applicable — documents live in git; the revision log records intent.
- Terminal and recovery states:
archiveddocuments are still loadable and reportable.
6. Interface Contracts¶
6.1 CLI (cmd/impact-model)¶
Flag conventions. -o / --out is the file a command writes, as in pandoc, marp, and mmdc; standard output is the default. -O / --observations names observation set files and may repeat. --as-of is the date an artifact describes. Exit codes are as in the table.
| Command | Description | Exit codes |
|---|---|---|
impact-model validate <file>... |
Schema + structural validation (kind auto-detected). --model <doc> checks observation sets against a theory; --parent <doc> checks that the given theories are children of a parent and that their contributesTo references resolve |
0 valid; 1 invalid; 2 usage / I/O error |
impact-model lint <toc> [--format table\|json] [--disable RULE] [--fail-on error\|warning] |
Deterministic quality rules; JSON is a SummaryReport |
0 GO; 1 NO-GO; 2 error |
impact-model graph <toc> [--format mermaid\|json\|dot] [--from ID] [--paths] |
Graph export and queries | 0; 2 |
impact-model status <toc> -O <obs.yaml>... [--as-of DATE] [--prev status.json] |
Compute status snapshot (JSON) and optional diff | 0; 2 |
impact-model report <kind> <toc> [-O <obs>...] [--as-of] [--pathway ID] [--audience ID] [--template file] [--out file] |
kind (status): exec, results, assumptions, interventions, evidence-gaps, story, learning-review, portfolio, narrative, actors, full, custom; kind (definition, no observations needed): pathway, logic-model, metrics, metric-selection, collection-plan, glossary, definitions (all definition reports) |
0; 2 |
impact-model import observations --csv <file> --map <mapping.yaml> --model <toc> [--out obs.yaml] |
CSV → ObservationSet; unmatched rows to stderr and --unmatched file |
0 all rows imported; 1 some rows unmatched; 2 error |
impact-model export csv <table> <toc> [-O obs...] |
table: indicators, metrics, metric-selection, collection-plan, glossary, results, themes |
0; 2 |
impact-model export okr\|journey\|v2mom\|dmaic <model> [-O obs...] |
PRISM exports | 0; 2 |
impact-model schema [name] |
List or print embedded schemas | 0; 2 |
impact-model init [--template program\|market] |
Write a starter document | 0; 2 |
impact-model mcp |
Start MCP server on stdio | 0 on clean shutdown |
impact-model patch <toc> <patch.json\|-> [--dry-run] [--allow-errors] |
Apply RFC 6902 patch atomically (§6.12) | 0 applied; 1 rejected; 2 error |
impact-model <kind> add\|set\|rm … (kind ∈ outcome, group, link, intervention, assumption, indicator, metric, pathway, actor, evidence, cause, term) |
Element commands compiled to patches; rm --cascade; all accept --dry-run |
0; 1 rejected; 2 |
impact-model fmt <file>... [--check] [--sort] |
Canonical serialization; --check exits 1 if a file would change |
0; 1; 2 |
impact-model diff <old> <new> [--format md\|json] [--revision] |
Semantic theory diff; optional draft Revision |
0; 2 |
impact-model deck <workshop\|exec\|learning-review\|story> <toc> [-O obs...] [--audience] [--export pdf\|pptx\|html] [--out file] |
Marp deck; export via external marp and mmdc when installed |
0; 2 (3 when export tool missing) |
impact-model judge show <toc> [--profile core\|development] [-O obs...] |
Emit judge bundle (§6.14) | 0; 2 |
impact-model judge record <toc> <verdict.json> [--judge NAME] |
Validate and store a verdict | 0; 1 invalid; 2 |
impact-model judge summary <toc> |
Aggregate stored verdicts with agreement | 0; 2 |
impact-model evidence add <toc> --url --title [--excerpt] [--accessed DATE] [--type] [--sensitivity] |
Element command for sources (compiles to a patch) | 0; 1; 2 |
impact-model export review-packet <toc> [--format md\|docx] [--reference-doc file] |
ID-tagged review document (§6.15) | 0; 2 (3 when pandoc missing for docx) |
impact-model export review-sheet <toc> [--out file.csv] |
Review rows as CSV | 0; 2 |
impact-model export docx\|pptx <kind> <toc> [-O obs...] [--reference-doc file] |
Any report (docx) or deck (pptx) through the pandoc profile |
0; 2; 3 |
impact-model import review <toc> <review.json>|--from-csv <sheet.csv> [--out proposed.patch.json] [--consultation-out c.json] |
Proposed patch + consultation; never writes the document | 0 all rows mapped; 1 rows need human decision or stale hash; 2 error |
impact-model approve <toc> --version LABEL --approver NAME[:ROLE]... [--note] [--force --reason] |
Sets approved, records approvers and revision |
0; 1 refused; 2 |
Global flags: --as-of (RFC 3339 date; default: today in UTC), --format,
--quiet. All machine output goes to stdout; diagnostics to stderr.
6.2 Library API (Go)¶
// pkg/impactio
func LoadDocument(path string) (*impact.Document, error)
func ParseDocument(data []byte, format Format) (*impact.Document, error)
func LoadObservations(paths ...string) ([]*impact.ObservationSet, error)
// pkg/validate
type Severity string // "error" | "warning" | "info"
type Issue struct {
Code string `json:"code"` // e.g. "VAL-REF-001"
Severity Severity `json:"severity"`
Path string `json:"path"` // JSON pointer, e.g. "/links/3/to"
Element string `json:"element,omitempty"`
Message string `json:"message"`
}
func Document(doc *impact.Document) []Issue
func Observations(doc *impact.Document, sets ...*impact.ObservationSet) []Issue
// pkg/graph
func Build(doc *impact.Document) (*Graph, error)
func (g *Graph) Upstream(id string) []string // transitive preconditions
func (g *Graph) Downstream(id string) []string // transitive consequences
func (g *Graph) PathsToImpact(id string, max int) [][]string
func (g *Graph) Cycles() [][]string // excludes feedback links
func (g *Graph) Levels() map[string]int // longest distance to impact
func (g *Graph) Orphans() []string // no incoming link and no intervention
func (g *Graph) DeadEnds() []string // no path to any impact node
func (g *Graph) BelowCeiling(id string) bool // within ceiling of accountability
// pkg/lint
type Rule interface { ID() string; Severity() validate.Severity; Check(*Context) []Finding }
func Run(doc *impact.Document, g *graph.Graph, opts Options) (*Result, error) // opts.Profile: draft|standard|full
func (r *Result) SummaryReport(project, version string) *summary.SummaryReport
// pkg/measure
type Options struct { AsOf time.Time; AtRiskTolerance float64; Rollup RollupMethod }
func Evaluate(doc *impact.Document, g *graph.Graph, obs []*impact.ObservationSet, opts Options) (*impact.Status, error)
func Diff(prev, cur *impact.Status) *impact.StatusDiff
// pkg/report
func BuildView(doc *impact.Document, g *graph.Graph, st *impact.Status, opts ViewOptions) (*View, error)
func Render(kind Kind, v *View) ([]byte, error)
func RenderTemplate(tmpl string, v *View) ([]byte, error)
func Mermaid(doc *impact.Document, st *impact.Status, opts MermaidOptions) string
// pkg/prismx
func ToOKR(doc *impact.Document, st *impact.Status) (*okr.OKRDocument, error)
func ToOutcomeJourneys(doc *impact.Document, st *impact.Status) ([]journey.OutcomeJourney, error)
func ToV2MOM(doc *impact.Model, st *impact.Status) (*v2mom.V2MOM, error) // wraps ErrNotV2MOMReady
func ToDMAIC(doc *impact.Model, st *impact.Status, sets []*impact.ObservationSet) (*dmaic.DMAICDocument, []Omission, error) // wraps ErrNotDMAICReady
Errors: functions return wrapped sentinel errors —
ErrInvalidDocument, ErrUnknownReference, ErrCycle (only from APIs that
require a DAG), ErrUnsupportedFormat. Callers use errors.Is.
6.3 MCP tools (pkg/mcp)¶
| Tool | Input | Output |
|---|---|---|
impact_schema |
{ name? } |
schema JSON or list of names |
impact_validate |
{ document \| path, kind? } |
{ valid, issues[] } |
impact_lint |
{ document \| path, disable?[] } |
SummaryReport JSON |
impact_graph |
{ document \| path, query: "paths"\|"upstream"\|"downstream"\|"cycles"\|"mermaid", id? } |
query result |
impact_status |
{ document \| path, observations[] \| paths[], asOf? } |
Status JSON |
impact_report |
{ document \| path, observations…, kind, asOf? } |
{ format: "markdown", content } |
impact_patch |
{ document \| path, patch[], dryRun?, allowErrors? } |
{ applied, document?, issuesIntroduced[], issuesResolved[] }; with path and not dryRun, writes canonical file |
impact_deck |
{ document \| path, observations…, kind, audience? } |
{ format: "marp", content } (no export) |
impact_export |
{ document \| path, observations…, kind: "okr"\|"journey"\|"v2mom"\|"dmaic", asOf? } |
{ kind, document, omissions?[] }; a model the format cannot be drawn from is refused with an error naming what to add |
impact_diff |
{ old: { document \| path }, new: { document \| path } } |
the structural diff |
impact_judge_bundle |
{ document \| path, profile? } |
judge bundle JSON |
Inline document is preferred for agents; path inputs MUST resolve inside
the server's configured root (--root, default: working directory) and MUST
reject .. escapes and symlinks leaving the root.
6.4 Lint rules (initial set)¶
Severity error blocks GO; warning is reported; info is advisory.
| Rule ID | Severity | Check |
|---|---|---|
| IMP-STR-001 | error | At least one outcome with level impact |
| IMP-STR-002 | error | No unintended cycles (cycles not marked feedback) |
| IMP-STR-003 | error | Every non-impact outcome has a path to an impact outcome (no dead ends) |
| IMP-STR-004 | warning | Every outcome has ≥ 1 precondition or a targeting intervention (no orphans) |
| IMP-STR-005 | warning | at_least logic has k between 1 and number of preconditions |
| IMP-STR-006 | warning | Links respect level order (no link from a higher level to a lower level unless feedback; same-level links allowed) |
| IMP-STR-007 | warning | Precondition groups are well-formed: ≥ 2 members, every member has a link into the group's target, no group cycles. (A nested group must target the same outcome as the group containing it; this part is a validation error, VAL-GRP-004.) |
| IMP-MET-003 | warning | A weighted_mean metric's weight metric is instantiated by an indicator in every pathway that uses the weighted metric, so the roll-up can be computed |
| IMP-PTH-003 | info | pathway.outcomeIds agree with the pathwayIds of the indicators measuring those outcomes |
| IMP-LOG-004 | info | Outcomes with ≥ 2 incoming links state their logic explicitly (non-default logic or a group), per Davies' AND/OR ambiguity critique |
| IMP-LOG-001 | warning | Every causal link into a medium_term, long_term, or impact outcome has a rationale |
| IMP-LOG-002 | warning | Every link into an outcome above short_term has ≥ 1 linked assumption |
| IMP-LOG-003 | info | Impact outcomes have ≥ 2 preconditions (single-chain theories are usually incomplete) |
| IMP-ASM-001 | warning | Every high/critical assumption has a test description |
| IMP-ASM-002 | warning | Every high/critical assumption cites evidence (assessments live in observation sets, which lint does not read) |
| IMP-ASM-003 | info | At least one contextual or external assumption exists |
| IMP-MEA-001 | error | When a ceiling of accountability is declared (IMP-ACC-001 asks for one), every outcome within it is measured: it has ≥ 1 indicator, or is named in another indicator's proxyFor |
| IMP-MEA-002 | warning | Every indicator has baseline, ≥ 1 target, data source, and frequency |
| IMP-MEA-003 | warning | Quantitative indicators have a unit and direction |
| IMP-MEA-004 | warning | Target dates fall within the document's time horizon |
| IMP-MEA-005 | warning | Timeline consistency: an outcome's final target date is not earlier than the final target dates of its preconditions |
| IMP-ACC-002 | warning | Outcomes above the ceiling of accountability are not explicitly marked responsibility: own (the organization contributes to them, it does not own them) |
| IMP-INT-001 | error | Every intervention targets ≥ 1 outcome (enforced by validation as VAL-INT-001, so it is never reached by lint) |
| IMP-INT-002 | warning | Every intervention has an owner actor |
| IMP-ACC-001 | warning | A ceiling of accountability is declared |
| IMP-ACT-001 | info | Outcomes in the influence sphere name ≥ 1 actor (boundary partner) |
| IMP-EVD-001 | info | Links into impact-level outcomes cite ≥ 1 evidence record |
| IMP-PTH-001 | warning | Every pathway contains ≥ 1 outcome with a path to an impact outcome |
| IMP-PTH-002 | info | Every pathway has ≥ 1 selected indicator |
| IMP-MET-001 | warning | Every common-layer metric is instantiated by an indicator in every pathway, or the omission is explained in the indicator selection rationale |
| IMP-MET-002 | error | rollupFrom is only used when the metric's aggregation is not none, and all roll-up sources instantiate the same metric (enforced by validation as VAL-MET-002, so it is never reached by lint) |
| IMP-CLM-001 | warning | Indicators measuring outcomes above the ceiling of accountability claim contribution, not attribution |
| IMP-BUD-001 | warning | Selected indicators' staff minutes per week ≤ measurementBudget.staffMinutesPerWeek |
| IMP-BUD-002 | warning | Survey / form questions per respondent per year ≤ maxQuestionsPerRespondentPerYear |
| IMP-BUD-003 | info | Number of selected indicators within targetSelectedMetrics |
| IMP-BUD-004 | info | Selected indicators declare collection.mode; passive and derived preferred over survey where an equivalent exists (reported as share passive) |
| IMP-AUD-001 | info | Every audience is targeted by ≥ 1 selected indicator or story theme |
| IMP-CAU-001 | warning | Every underlying and structural cause is addressed by ≥ 1 outcome or marked outOfScope with a reason |
| IMP-CAU-002 | info | When causes are declared, outcomes address ≥ 1 non-immediate cause (root causes, not only immediate ones) |
| IMP-CAU-003 | warning | A cause contributes only to causes at its own level or nearer the problem (structural → underlying → immediate); a parent at a deeper level usually means the link was written backwards |
| IMP-RSP-001 | warning | Outcomes with responsibility partner or shared name ≥ 1 delivering actor; shared names ≥ 2 |
| IMP-RSP-002 | info | partner and unaddressed outcomes on a path to impact still have ≥ 1 indicator (monitor the whole solution tree) |
| IMP-ASM-004 | warning | Assumptions with needsTesting: true have a test and either a monitoring indicator or a linked decision / evaluation question |
| IMP-ASM-005 | info | Assumptions of type partnership exist when any outcome is shared or partner |
| IMP-RSK-001 | info | high / critical assumptions have ≥ 1 related risk with mitigation |
| IMP-EQU-001 | warning | Indicators on outcomes that target groups disaggregate by those groups (specify changes for target groups, not assume them) |
| IMP-VAL-001 | info | Each of focus, change_analysis, assumptions_risks, actors has ≥ 1 consultation record |
| IMP-PRV-001 | warning (full profile only) |
Every element declares a provenance basis |
| IMP-PRV-002 | warning | Every stated element cites ≥ 1 evidence record with a URL or document reference |
| IMP-PRV-003 | warning | An approved document has no hypothesis elements at accountable levels (responsibility own or shared) |
| IMP-PRV-004 | info | Selected indicators declare dataAvailability |
| IMP-DMA-001 | warning | An indicator of a pathway marked projections: [dmaic] declares a frequency and a data source (dataSource or collection.system) |
| IMP-DMA-002 | warning | An indicator of a DMAIC pathway is measured ≥ 20 times a year, so control limits can be set within about a year |
| IMP-DMA-003 | warning | A survey indicator of a DMAIC pathway gets ≥ 30 responses per measurement (collection.respondentsPerYear × sampleRate ÷ measurements per year), and states respondentsPerYear |
| IMP-EFF-001 | warning | Every effect is relieved by ≥ 1 outcome (addressesEffects) or marked outOfScope with a reason |
| IMP-EFF-002 | info | Outcomes that relieve effects include ≥ 1 at long_term or impact level (effects are what the program ultimately reduces; causes are what it works on) |
Rule IDs are permanent. Retired rules keep their ID reserved.
6.5 Measurement algorithms¶
Indicator progress (quantitative). With baseline b, current c, final
target t, direction d:
increase:p = (c − b) / (t − b);decrease:p = (b − c) / (b − t).maintain:p = 1if|c − t| ≤ toleranceelse0.- If
t = b,p = 1when the target condition holds, else 0. pis clamped to[0, 1.5]for display;achievedwhenp ≥ 1.
Expected progress at asOf. Targets form a time series
(baseline.date, 0) → (target_i.date, p_i). Expected e is linear
interpolation at asOf (0 before the baseline date, 1 after the final
target). Indicator status:
| Condition | Status |
|---|---|
| no targets defined (baseline only) | baseline_only (latest value reported, no progress computed) |
no observation and asOf < first target date |
not_started |
no observation and asOf ≥ first target date |
no_data |
last observation older than 2 × frequency |
stale |
p ≥ 1 |
achieved |
p ≥ e − tol (default tol = 0.10) |
on_track |
p ≥ e − 2·tol |
at_risk |
| otherwise | off_track |
Qualitative indicators use the ordinal position of the observed level in the
indicator's ordered levels as c, with b and t as level positions.
Health mapping (prism-core): achieved, on_track → green;
at_risk, stale → yellow; off_track → red; not_started, no_data,
baseline_only → no health (rendered grey). baseline_only ranks with
not_started in roll-ups.
Metric roll-ups. For an indicator with rollupFrom, the value at a date
is computed from the latest source observations at or before that date using
the metric's aggregation (weighted_mean weights by the latest value of
weightMetricId's indicator in the same pathway). If any source lacks a value
the roll-up is no_data and lists the missing sources. Direct observations of
a roll-up indicator take precedence and the computed value is reported
alongside for comparison.
Theme counts. A theme_count indicator's value for a period is the number
of stories in that period's observation sets tagged with any of its
themeIds (optionally filtered by pathwayIds); stories are counted once.
Collection burden (pkg/burden). Per selected indicator, staff minutes
are normalized to minutes per week from collection.minutesPerPeriod and
collection.period; respondent questions per year =
questionsPerRespondent × (collections per year) × sampleRate. Totals are
reported overall, per pathway, and per mode; budget comparisons feed
TOC-BUD-*.
Outcome status = roll-up of its indicators by Rollup option: worst
(default) or mean of progress. Outcomes without indicators get unmeasured.
Pathway status. Processed in topological order from the leaves upward. A
node's precondition signal is computed over incoming non-feedback links:
grouped sources are first reduced through their PreconditionGroup (nested
groups innermost first), then the group results and ungrouped sources are
combined with the outcome's logic: all → worst; any → best;
at_least k → k-th best. Status ordering for worst/best is
off_track < at_risk < stale < no_data < not_started < on_track < achieved. Pathway status = worse of (own outcome status,
precondition signal), where unmeasured defers to the precondition signal.
Assumption propagation. Effective assumption status = latest assessment
in observation sets, else the document's static status. A link whose
assumptions include invalidated is broken; at_risk makes it at_risk.
Every outcome downstream of a broken link gets the flag
assumptionBroken: [assumptionIDs] and its pathway status is capped at
at_risk (critical assumption: off_track).
Link confidence ∈ [0, 1]: 0.4·evidence + 0.4·assumptions + 0.2·rationale,
where evidence = max strength of supporting evidence (strong 1.0,
moderate 0.7, weak 0.4, anecdotal 0.2, none 0) minus half the max
strength of contradicting evidence, floored at 0, assumptions = min over linked assumption statuses
(validated 1.0, holding 0.8, untested 0.5, at_risk 0.25,
invalidated 0), and rationale = 1 if present else 0. Path confidence =
product of link confidences along the path; an outcome's confidence = max over
its paths to impact. Weights are options with these defaults and are printed
in every report that shows a confidence value (INV-5).
Diff. Diff(prev, cur) lists status changes for indicators, outcomes,
assumptions, and pathways, ordered per INV-3.
6.6 Reports¶
| Kind | Content |
|---|---|
exec |
Headline (goal, overall pathway status, as-of), status by level, top 5 at-risk / off-track outcomes with driving indicators, broken or untested critical assumptions, changes since previous snapshot, decisions requested (from Model.decisions and auto-generated prompts for broken assumptions), confidence footnote |
pathway |
Mermaid flowchart BT with subgraphs by level, nodes styled by health, dashed feedback links, ceiling of accountability as a subgraph boundary |
results |
Results framework: outcome → indicator → baseline → targets → latest → status |
assumptions |
Assumption register: statement, type, criticality, applies to, test, evidence, status |
interventions |
Intervention → owner → status → targeted outcomes → pathway status |
evidence-gaps |
Links and critical assumptions lacking evidence; outcomes lacking indicators |
story |
For one --audience: audience needs, the indicators and outcomes tagged for it with latest values and status, consented stories tagged for it (summary always; quote only per TRD-023), theme counts, and caveats — a narrative scaffold for communications staff to finish |
learning-review |
For a period: status changes since previous snapshot, largest deviations from expected progress (surprises), assumption status changes, theme counts and new themes, data-quality notes, and draft questions; merges any recorded LearningReview for the period |
portfolio |
Per pathway: pathway status, outcome-status distribution, confidence, collection burden, share of passive data, audiences covered, and open decisions — evidence for grow / adjust / sunset decisions, without a recommendation |
narrative |
Programme / strategy document scaffold following the UNDG section mapping (research §9.7): causal analysis (problem tree → outcomes addressing each cause level), priorities and rationale (priority scores), assumptions and risks with mitigation, partner-delivered and unaddressed elements, implementation arrangements (who does what), M&E provisions (assumptions needing testing, monitoring cadence, evaluation questions from evidence gaps), validation trail (consultations), key messages. Works without observations; adds status when given |
assessment |
Definition-phase assessment (no observations needed): provenance mix with lists, outcomes without indicators, indicators by data availability, assumptions needing testing, open decisions, generated questions for the organization, short narrative summary. Inferred and hypothesis elements are marked in every report and deck by a basis badge |
actors |
Division of labour: actors × outcomes / interventions with roles; outcomes needing collaboration; outcomes with no delivering actor; partner-delivered outcomes being monitored |
full |
All status reports in one document |
custom |
User text/template over report.View |
Definition reports (no observations required):
| Kind | Content |
|---|---|
pathway |
As above, without status styling when no status is given; --pathway restricts to one pathway plus its route to impact |
logic-model |
--diagram adds a left-to-right Mermaid flow of the same stages. Per pathway: inputs (intervention resources) → activities (interventions) → outputs (output-level outcomes and intervention outputs) → outcomes (short / medium / long-term) → impact, as a table |
outcome-map |
The outcomes framework: per pathway, outcomes grouped by level from impact down, each with sphere, category, change type, responsibility and delivering actors, the causes it addresses, and its indicators; outcomes above the ceiling of accountability and monitored-only outcomes are marked |
logframe |
Logframe matrix per pathway: rows goal (impact), purpose (long-term and medium-term outcomes), outputs (short-term and output outcomes), activities (interventions); columns narrative, indicators with targets, means of verification (indicator data source and method), and assumptions (those applying to the row's links and interventions) |
metrics |
Metric definitions: name, definition, formula, unit, aggregation, layer, and which pathways instantiate each |
metric-selection |
Candidate indicators × selection criteria with weighted score, status, rationale; sorted by score |
collection-plan |
Selected indicators: definition, source system, mode, owner, frequency, minutes per period, questions per respondent; totals vs budget |
glossary |
Glossary terms and theme codebook |
problem-tree |
Markdown: the problem tree as a Mermaid flowchart BT (causes in rows below the core problem, effects in rows above, arrows upward), then a table of causes and one of effects, each with its level or parents and the outcomes that address it or why it is set aside |
problem-tree-d2 |
The same diagram in D2, whose grid keeps each row in place |
problem-tree-json |
The diagram document itself: the d2vision diagram spec (nodes with row, edges) that problem-tree and problem-tree-d2 are rendered from |
definitions |
The workshop pack, in this order: the problem tree (Mermaid, only when the model has a live cause or effect), causal pathways, logic models, outcome maps, logframes, metric definitions, glossary; each section is the matching report verbatim |
Problem tree layering: the core problem is row 0. A cause with no parents is in
row −1; any other cause is one row below its lowest parent (row = min(parent
rows) − 1), so every arrow from a cause points up. Effects mirror this upward:
an effect with no parents is in row +1, any other is one row above its highest
parent. Rejected causes and effects are left out. Within a row, nodes keep file
order.
report.View is a documented, versioned struct (ViewVersion = "1");
changing field names is a breaking change.
6.7 Reference grammar (pkg/ref)¶
ref = "model:" docSlug [ "#" elementId ]
docSlug = slug ; e.g. model:agent-auth-open-standard
elementId= slug ; e.g. model:agent-auth-open-standard#oc-idp-support
slug = [a-z][a-z0-9]*(-[a-z0-9]+)* ; ≤ 128 chars
Element IDs inside a document are slugs (INV-1). External references use
ExternalRef { framework, ref, url?, relation }; ref is opaque to this
module (for market-spec it is that repo's {type}:{slug} form). This grammar
is the USM/UFM integration contract together with §7.3.
6.8 Error Semantics¶
Not applicable as HTTP — there is no network API. CLI exit codes are in §6.1.
MCP tool errors return isError: true with a JSON body
{ code, message, issues? }; codes mirror sentinel errors
(invalid_document, unknown_reference, unsupported_format, path_outside_root).
6.9 Versioning¶
- Schema
$id:https://github.com/ProductBuildersHQ/impact-model/schema/v0/<name>.schema.json. - Every document carries
kind(ImpactModel|ObservationSet|ImpactModelStatus) andspecVersion("0.1"). - Within v0.x, minor versions are additive; removals or renames require a major bump and a migration note in the CHANGELOG.
6.10 Events¶
Not applicable — no event bus; file outputs only.
6.11 CSV mapping and agent skills¶
CSV mapping file (YAML, schema-defined as csv-mapping):
modelId: example-network
columns:
indicator: metric_id # column holding indicator ID, or…
indicatorFixed: "" # …a fixed indicator ID for single-indicator sheets
date: period_end
value: value
pathway: program_area # optional
quality: data_quality # optional
source: source # optional
indicatorAliases: # map spreadsheet labels to indicator IDs
"Attendee hours": ind-attendee-hours
dateFormat: "2006-01-02"
Rows whose indicator, date, or value cannot be resolved are written to the unmatched list with row number and reason (INV-4).
Agent skills (skills/<name>/SKILL.md plus templates). Each skill
states purpose, inputs, the exact command sequence, checks to report, and
outputs. AI judgment is limited to drafting text and asking the user; every
number comes from a command.
| Skill | Command sequence (abridged) | Checks reported |
|---|---|---|
impact-author |
impact-model init → edit → impact-model validate → impact-model lint → impact-model report definitions |
validation issues, lint GO/NO-GO, definition pack written |
impact-status-refresh |
impact-model import observations --csv → impact-model validate → impact-model status --prev → impact-model report exec |
rows imported / unmatched, indicators with new data, changes vs previous snapshot |
impact-learning-review |
impact-model status → impact-model report learning-review → assistant drafts discussion questions |
surprises listed, assumptions changed |
impact-audience-story |
impact-model report story --audience → assistant drafts narrative from the scaffold |
every number in the draft present in the scaffold; no unconsented quotes |
impact-draft-from-sources |
impact-model init → for each source: assistant reads it, impact-model evidence add --url … --excerpt … → assistant proposes elements → impact-model patch (each element with provenance.basis and evidenceIds) → impact-model validate → impact-model lint --profile draft → impact-model report definitions and impact-model report assessment |
sources recorded, elements by basis, stated elements without citation (must be 0), lint GO under draft |
toc-share-google |
impact-model export review-packet --format docx, impact-model export review-sheet, impact-model deck … --export / impact-model export pptx → upload with conversion (gogoogle CLI drive upload --convert) → after review: read Sheet records, Doc tables, comments, and suggestions (gogoogle CLI or mcp-google tools) → assistant writes ReviewResponse → impact-model import review → human runs impact-model patch on accepted proposals |
files published with links; rows read per origin; stale-hash warnings; rows needing human decision |
Manual fallback for toc-share-google: upload the DOCX and CSV by hand, ask
reviewers to use the response columns, download the sheet as CSV, and convert
it to a ReviewResponse with impact-model import review --from-csv.
Skills that change the theory (impact-author) MUST use impact-model patch or element
commands, never write the file directly.
6.12 Editing contract (pkg/edit)¶
type Patch []Op // RFC 6902: add, remove, replace, move, copy, test
type Result struct {
Document *impact.Document
IssuesIntroduced []validate.Issue
IssuesResolved []validate.Issue
Applied bool
}
func Apply(doc *impact.Document, p Patch, opts Options) (*Result, error) // opts.AllowErrors
- Addressing. Standard JSON Pointers plus an ID segment:
@<id>selects the element with thatidin the array, e.g./outcomes/@oc-adoption/titleor/links/@ln-3. ID segments are resolved to indexes before applying; an unknown ID fails the whole patch. - Atomicity. Operations apply to a deep copy (INV-2). If any operation
fails, or the result fails schema validation, or
error-severity issues increase versus the input (unlessAllowErrors),Appliedis false and the original is untouched. - Element commands build patches:
addappends (/outcomes/-) after checking ID uniqueness;setemitsreplace/addper flag;rmemitsremoveand, with--cascade, removes or rewrites references (links touching a removed outcome; IDs inappliesTo,assumptionIds, etc.). Without--cascade, dangling references make the patch rejected (INV-4). - Writing. The CLI writes the result in canonical form (
impact-model fmt), in the input's format. YAML comments are not preserved (PRD R-3). - Canonical form. JSON: two-space indent, struct field order, no HTML
escaping, trailing newline. YAML: generated from canonical JSON. Element
order is preserved;
--sortorders elements by ID.
6.13 Presentations (pkg/slides)¶
Decks are Marp Markdown with a front-matter theme (default, overridable by
--theme file.css) and one idea per slide.
| Deck | Slides |
|---|---|
workshop |
Title · problem statement and causes · long-term goal and ceiling of accountability · pathway diagram (whole, then per pathway) · logic model per pathway · key assumptions (if / then / because) · metric definitions · glossary · open questions and decisions |
exec |
Headline status · pathway diagram with health · top risks and broken assumptions · what changed since last period · decisions requested · confidence and method footnote |
learning-review |
Period summary · surprises · assumption changes · theme counts and selected stories (consented) · questions for discussion · actions |
story |
Audience need · three to five headline metrics with caveats · one or two consented stories · call to action |
Diagrams: the library embeds Mermaid source in fenced blocks and, when the
CLI is asked to export, cmd/impact-model renders each to SVG with mmdc and rewrites
the slide to reference the SVG; then runs marp --pdf|--pptx|--html. Missing
tools produce exit code 3 with install guidance; the Markdown deck is still
written. Same layout conventions as prism-maturity's Marp generator.
6.14 Judging (pkg/judge)¶
- Bundle (
impact-model judge show): JSON withrubric(core or developmentRubricSet),document(canonical ToC JSON),narrative(generatednarrativereport Markdown),lint(SummaryReport),instructions(judge instructions fromspec/), andbundleHash(SHA-256 of the inputs). The judge (an agent session, visionspec, or visionstudio) reads the bundle; this module makes no model calls. - Verdict (
impact-model judge record): a structured-evaluation evaluation report whosebundleHashmatches; stored as<toc-dir>/.toc/judgements/<bundleHash>/<judge>.json. Mismatched hashes are rejected (the verdict was for a different version). - Summary (
impact-model judge summary): per category, verdict distribution and inter-rater agreement using structured-evaluation's multi-judge and IRR helpers; overall pass requires the rubric's pass criteria under the configured aggregation (default: majority, ties →partial).
6.15 Sharing, review, and approval (pkg/share, pkg/review)¶
Export stamp. Every export includes
{modelId, version, contentHash, asOf}, where contentHash = SHA-256 of
canonical JSON (§6.12). Where it goes:
| Format | Location |
|---|---|
| Markdown | YAML metadata block |
| DOCX / PPTX | Inherited from the metadata block, plus a footer line |
| CSV | A leading comment row # model:<id> version:<v> hash:<h> |
| Decks | Title slide |
pandoc profile.
- Mermaid diagrams are rendered to PNG by
mmdc(CLI only) and referenced by relative path. - Pipe tables only, with no nested lists inside cells.
\newpagemarkers between sections.- The basis badge is rendered as text:
[stated],[inferred],[hypothesis]. impact-model export docx|pptxrunspandoc -f markdown -t docx|pptx [--reference-doc]with an argument vector (no shell).
Review packet and sheet.
- Packet layout: one section per element kind (outcomes, links /
assumptions, interventions, indicators, metrics, actors). Each section has
one table with columns
ID | Statement | Basis | Sources | Decision | Suggested change | Comment, with the last three empty. - Instructions: the packet asks reviewers to answer in the response columns. Comments and suggestions are still accepted and are mapped by quoted text.
- Sheet: the same rows, one row per element, with a
kindcolumn.
ReviewResponse (schema review-response):
type ReviewResponse struct {
Kind string `json:"kind"` // "ReviewResponse"
ModelID string `json:"modelId"`
Version string `json:"version"`
ContentHash string `json:"contentHash"`
Reviewers []string `json:"reviewers"` // groups or roles, e.g. "program staff"
Method string `json:"method"` // document_review | sheet_review | workshop
Date string `json:"date"`
Rows []ReviewRow `json:"rows"`
}
type ReviewRow struct {
ElementID string `json:"elementId,omitempty"` // empty when only quoted text is known
Decision string `json:"decision,omitempty"` // agree | change | remove | unsure
SuggestedChange string `json:"suggestedChange,omitempty"`
Comment string `json:"comment,omitempty"`
Origin string `json:"origin"` // sheet | table | comment | suggestion
QuotedText string `json:"quotedText,omitempty"`
}
Import mapping (impact-model import review):
| Row | Proposed op |
|---|---|
agree |
replace /…/@id/provenance/basis → confirmed |
remove |
basis → rejected |
change with a structured SuggestedChange (field: value lines for title, statement, level, definition, …) |
replace ops |
change with free text, unsure, or comment-only |
Flagged note for a human, not an op |
No element ID but QuotedText |
Resolved by exact match against rendered statements. If ambiguous or not found, flagged |
| Stale hash (response hash ≠ document hash) | Every row flagged; ops still proposed against current IDs |
The import also writes a Consultation record (step whole unless given;
method; reviewer groups; findings = row counts by decision).
Approval. impact-model approve builds a patch that sets:
metadata.status = approvedmetadata.version- appended
Revision{trigger: review, summary, approvers}(prism-coreApprover)
It applies the patch through pkg/edit. It refuses when:
- lint reports any
errorfinding; - any rule IMP-PRV-003 violation remains, unless
--forcewith--reason(recorded in the revision).
7. Data Design¶
7.1 Document model (pkg/impact)¶
Go structs are normative; this table summarizes them. All slices are optional unless marked required.
type Document struct {
Schema string `json:"$schema,omitempty"`
Kind string `json:"kind"` // "ImpactModel"
SpecVersion string `json:"specVersion"` // "0.1"
ID string `json:"id"` // doc slug
Metadata core.Metadata `json:"metadata"` // prism-core
Perspective *Perspective `json:"perspective,omitempty"`
Context Context `json:"context"`
Ceiling *Ceiling `json:"ceilingOfAccountability,omitempty"`
Outcomes []Outcome `json:"outcomes"`
Groups []PreconditionGroup `json:"preconditionGroups,omitempty"`
Links []CausalLink `json:"links,omitempty"`
Interventions []Intervention `json:"interventions,omitempty"`
Assumptions []Assumption `json:"assumptions,omitempty"`
Indicators []Indicator `json:"indicators,omitempty"`
Actors []Actor `json:"actors,omitempty"`
ActorRegistry string `json:"actorRegistry,omitempty"` // ref to shared registry doc
Evidence []Evidence `json:"evidence,omitempty"`
ExternalFactors []ExternalFactor `json:"externalFactors,omitempty"`
Risks []Risk `json:"risks,omitempty"`
Alignments []Alignment `json:"alignments,omitempty"`
Decisions []Decision `json:"decisions,omitempty"`
Revisions []Revision `json:"revisions,omitempty"`
// Measurement-framework design (portfolio, sustainability, story)
Parent string `json:"parent,omitempty"` // model: ref of parent ToC
Pathways []Pathway `json:"pathways,omitempty"`
Metrics []Metric `json:"metrics,omitempty"`
SelectionCriteria []SelectionCriterion `json:"selectionCriteria,omitempty"`
Budget *MeasurementBudget `json:"measurementBudget,omitempty"`
Audiences []Audience `json:"audiences,omitempty"`
Glossary []GlossaryTerm `json:"glossary,omitempty"`
Themes []Theme `json:"themes,omitempty"` // story codebook
// Change analysis and validation (UNDG)
Causes []Cause `json:"causes,omitempty"` // problem tree
TargetGroups []TargetGroup `json:"targetGroups,omitempty"` // priority populations
Consultations []Consultation `json:"consultations,omitempty"` // validation record
}
| Type | Key fields | Notes |
|---|---|---|
Perspective |
actorId, role (proponent, funder, implementer, observer), futureRef? |
Whose theory this is. USM uses the actor; UFM uses futureRef to group a ToC under a competing future. |
Context |
problemStatement (req), rootCauses[], targetPopulation, market? (ExternalRef), geography?, timeHorizon {start,end} (optional, keeping the minimal document small; date-range lint rules apply only when set), mission, vision, values[] {name (req), description} (ordered, most important first; a value needs a name and names are unique: VAL-DOC-006, VAL-DOC-007), narrative |
Problem analysis / problem tree inputs. Mission, vision, and values frame the model and are not outcomes; the V2MOM export needs the vision and at least one value. |
Ceiling |
level (outcome level) or outcomeIds[], rationale |
Ceiling of accountability; outcomes above it are tracked but not owned. |
Outcome |
id, title (req; a positive condition, not an activity), description, level (req: impact, long_term, medium_term, short_term, output), sphere (control, influence, interest), logic {type: all/any/at_least, k} over ungrouped incoming links (default all), category (open string; recommended vocabularies: ORS impact/influence/leverage/capacity, COM-B reach/capability/opportunity/motivation/behavior/direct_benefit/wellbeing, MSD AAER adopt/adapt/expand/respond), changeType (knowledge, attitude, skill, behavior, practice, policy, norm, condition, investment, adoption), population, actorIds[] (who changes / co-owners — supports collective outcomes), contributesTo[] (outcomes in a parent theory, as model:<doc>#<outcome> references; resolved by validate --parent / validate.Family), responsibility (own default, shared, partner, unaddressed), deliveredBy[] (actor IDs), addressesCauses[] (cause IDs), addressesEffects[] (effect IDs), targetGroupIds[], priority? {scores[] {criterionId, score 1–5, note}, rationale}, tags[] |
Graph node. Indicators reference outcomes, not vice versa. partner / unaddressed outcomes stay in the graph and are monitored (UNDG: monitor the whole solution tree) but are excluded from accountability counts. |
Cause |
id, statement (req), level (req: immediate, underlying, structural), parents[] (cause IDs it contributes to; empty for a direct cause of the core problem), affectedGroupIds[], evidenceIds[], outOfScope? {reason}, provenance? |
Problem tree roots; outcomes map to causes via addressesCauses. Causes form a network; validation rejects a cause among its own parents (VAL-CAU-003) and a parent chain that loops (VAL-CAU-004). |
Effect |
id, statement (req), parents[] (effect IDs it follows from; empty for a direct effect of the core problem), affectedGroupIds[], evidenceIds[], outOfScope? {reason}, provenance? |
Problem tree branches; outcomes map to effects via addressesEffects. Validation: a statement (VAL-EFF-001), no effect among its own parents (VAL-EFF-002), no looping parent chain (VAL-EFF-003), a reason when set aside (VAL-EFF-004). The core problem is context.problemStatement. |
TargetGroup |
id, name (req), description, size?, actorIds[] |
Priority population (e.g. under-resourced member organizations, residents of low-access areas). |
Consultation |
id, date (req), step (req: focus, change_analysis, assumptions_risks, actors, measurement, whole), method (workshop, interview, survey, document_review, evidence_review, meeting), stakeholderGroups[] (groups or actor IDs, never personal names required), count?, findings[], changedIds[] |
Validation trail per UNDG principle (a) and the validate-each-step loop. |
PreconditionGroup |
id, target (outcome ID), members[] (outcome or group IDs; ≥ 2), logic {all/any/at_least, k}, sufficiency (text) |
Explicit AND/OR/k-of-n "cluster" (ActKnowledge). Groups nest, so (A AND B) OR C is expressible. Incoming links whose source is a group member are evaluated through the group; the group's result feeds the target's logic. |
CausalLink |
id, from (outcome or intervention ID), to (outcome ID), type (causes, enables, contributes_to, necessary_condition), rationale, assumptionIds[], evidenceIds[], interventionIds[] (interventions placed on the connector, ActKnowledge style), polarity (positive default, negative), feedback bool, loopType (reinforcing, balancing; only with feedback), lag? (ISO 8601 duration) |
Directed edge. Intervention → outcome edges MAY be implicit via Intervention.targets. |
Intervention |
id, title (req), type (program, product, policy, advocacy, partnership, standardization, investment, communication, research, other), targets[] (req), outputs[], ownerActorId, status, capabilities[] (core.CapabilityRef), initiativeRefs[] (ExternalRef, e.g. visionstudio INIT-…), resources |
Strategy / activity. |
Assumption |
id, statement (req), ifThenBecause? {if, then, because}, type (causal, contextual, implementation, behavioral, external, partnership), appliesTo[] (link/outcome/intervention IDs), criticality (low, medium, high, critical), plausibility (low, medium, high), explainsFully? (bool; UNDG question 1), needsTesting (bool; UNDG question 3), test (how: monitoring indicator, evaluation, research), indicatorIds[] (assumption-monitoring indicators), evidenceIds[], status, ownerActorId |
Static status is the default; observation sets override. confidence from v0.1 drafts is replaced by plausibility (UNDG question 2). |
Indicator |
id, measures (req; outcome ID, or intervention ID for output indicators, or assumption ID for assumption-monitoring indicators, or pathway ID for pathway-level and roll-up indicators that have no single outcome), name (req), definition, measurementType (quantitative, qualitative, hybrid), unit, direction (increase, decrease, maintain), tolerance, levels[] (ordered qualitative levels; Outcome Mapping progress markers use expect/like/love), baseline {value, level, date, source}, targets[] {value, level, threshold, date, label, milestone bool}, population {description, actorIds[], size}, disaggregation[], dataSource, method, frequency (ISO 8601 duration), ownerActorId, proxyFor[] (outcome IDs this indicator stands in for), externalRefs[] (e.g. maturity SLI, OKR KR) |
ActKnowledge's four components — population, target, threshold, timeline — map to population, targets[].value, targets[].threshold, targets[].date. Framework-design extensions: metricId (instantiates a shared Metric), pathwayIds[], claim (attribution or contribution; contribution when omitted, so a report never overclaims), caveats[], collection {mode, system, minutesPerPeriod, period, questionsPerRespondent, respondentsPerYear, sampleRate}, selection {status: candidate/selected/retired, scores[] {criterionId, score 1–5, note}, rationale}, audienceIds[], rollupFrom[] (indicator IDs whose values roll up into this one via the metric's rule), themeIds[] (for theme_count indicators derived from stories). measurementType gains theme_count together with the story codebook (RMI-035). |
Pathway |
id, title (req), kind (program, strategy, theme, geography, segment), description, ownerActorId, outcomeIds[], interventionIds[], childModel? (ref to a program-level ToC document), focus?, projections[] (dmaic; unique, VAL-PTH-007) |
Program area / strategy grouping. projections: [dmaic] subjects the pathway's indicators to the IMP-DMA rules and includes it in export dmaic. Indicators join via pathwayIds. A pathway may delegate detail to a child ToC whose parent points back. |
Metric |
id, name (req), definition (req), formula (req; exact computation in words, e.g. "sum over sessions of attendees × session hours"), unit, direction, aggregation (sum, mean, weighted_mean, median, mode, min, max, latest, none; median and mode are the honest choices for ordinal levels), weightMetricId?, layer (common, pathway) |
Shared definition so a metric is computed identically across pathways. none means values cannot be combined (e.g. distinct counts across overlapping populations) and org-level values must be observed directly. |
SelectionCriterion |
id, name, question, weight, appliesTo (req: indicator, outcome, intervention) |
Indicator criteria: decision value, audience value, cost to collect (inverted), consistency, credibility. Outcome / intervention (focus and solution) criteria: equity, comparative advantage, feasibility, evidence of what works. Scores live on the subject (selection.scores for indicators, priority.scores for outcomes and interventions). |
MeasurementBudget |
staffMinutesPerWeek, maxQuestionsPerRespondentPerYear, targetSelectedMetrics {min, max} |
Declared sustainability constraints. |
Audience |
id, name (req), needs (what they need to see), channels[] (e.g. annual report, grant proposal, board deck, newsletter) |
Story and reporting target. |
GlossaryTerm |
id (req, so terms can be addressed and referenced), term (req), definition (req), examples[], relatedIds[] |
Shared vocabulary (e.g. output vs outcome vs impact with organization-specific examples). |
Theme |
id, label (req), definition (req), includes[], excludes[], examples[], outcomeIds[] |
Codebook for coding stories consistently (human or AI). |
Provenance (embedded as provenance on every element type) |
basis (stated, inferred, hypothesis, confirmed, rejected), evidenceIds[], note, decidedAt?, decidedBy? (group or role) |
Public-source drafting and review lifecycle. rejected elements stay in the document for history but are excluded from graph analysis, measurement, and default reports. Sources are ordinary Evidence records (URL, title, excerpt, accessedAt, sensitivity). |
Indicator dataAvailability |
public, internal_existing, internal_new, partner_held, unknown |
Feeds the assessment report and passive-data-first selection. |
Actor |
id, name (req), type (organization, person, group, community, customer_segment, government, standards_body, regulator, vendor, platform, investor, other), roles[] (implementer, beneficiary, boundary_partner, funder, champion, influencer, competitor, adopter, gatekeeper), stance (champion, supporter, neutral, skeptic, opponent, unknown), influence/interest (low/medium/high), contact (core.Person), externalRefs[] |
Shared registry supported via ActorRegistry document. |
Evidence |
id, title (req), type (research, evaluation, monitoring_data, expert_opinion, stakeholder_testimony, case_study, internal_metric, market_signal, outcome_harvest, anecdote), strength (strong, moderate, weak, anecdotal), direction (supports default, contradicts), processTracingTest (straw_in_the_wind, hoop, smoking_gun, doubly_decisive; optional), source, url, summary, excerpt, sensitivity (public, internal, restricted; zero value treated as restricted), accessedAt |
Mirrors prism-roadmap evidence sensitivity semantics. Contradicting evidence lowers link confidence (§6.5). |
ExternalFactor |
id, description (req), kind (enabler, opportunity, constraint, trend, shock), affects[], likelihood, trend (prism-core trend) |
Context conditions outside the ToC's control. |
Risk |
embeds core.Risk, plus appliesTo[], category (environmental_political, opportunity, design, partnership, market, technical, operational), relatedAssumptionIds[] |
prism-core reuse; UNDG notes assumptions and risks are often inverse, so the link is explicit. opportunity risks are upside. |
Alignment |
elementId, target (ExternalRef) |
ExternalRef{framework, ref, url, relation}; frameworks: okr, bsc, v2mom, capability, maturity_sli, market_spec, usm, ufm, sdg, vsem (Vision–Strategy–Execution–Metrics one-pagers), visionstudio, custom; relations: supports, measures, implements, contributes_to, same_as. BSC alignments carry perspective in ExternalRef.attributes (financial, customer, internal_process, learning_growth). |
Decision |
id, question, options[], dueDate, ownerActorId, status, relatedIds[] |
Feeds "decisions requested" in exec reports. |
Revision |
version, date, summary, trigger (learning, evidence, context_change, strategy_change, review), changedIds[], triggerEvidenceIds[], qualityReview? {plausible, feasible, testable: pass/partial/fail, notes} |
Adaptive-management log; qualityReview records the Aspen Roundtable criteria. |
7.2 Observation set and status¶
type ObservationSet struct {
Kind string `json:"kind"` // "ObservationSet"
SpecVersion string `json:"specVersion"`
ModelID string `json:"modelId"` // doc slug it measures
Period string `json:"period,omitempty"` // e.g. "2026-Q4"
Observations []Observation `json:"observations,omitempty"`
Assessments []AssumptionAssessment `json:"assumptionAssessments,omitempty"`
Stories []Story `json:"stories,omitempty"`
Reviews []LearningReview `json:"learningReviews,omitempty"`
Notes []Note `json:"notes,omitempty"`
}
type Story struct {
ID string `json:"id"`
Date string `json:"date"`
Title string `json:"title"`
Summary string `json:"summary"` // paraphrase, safe to render
Quote string `json:"quote,omitempty"` // rendered only with consent
ActorID string `json:"actorId,omitempty"` // who changed (may be anonymized)
Domain string `json:"domain,omitempty"` // domain of change
ThemeIDs []string `json:"themeIds,omitempty"` // must exist in codebook
OutcomeIDs []string `json:"outcomeIds,omitempty"`
AudienceIDs []string `json:"audienceIds,omitempty"`
Consent string `json:"consent"` // granted | not_granted | unknown
Sensitivity string `json:"sensitivity"` // public | internal | restricted
CodedBy string `json:"codedBy,omitempty"` // person | model:<name> — provenance of theme coding
Significance string `json:"significance,omitempty"` // why it was selected (MSC)
}
type LearningReview struct {
Period string `json:"period"`
Questions []string `json:"questions,omitempty"`
Findings []string `json:"findings,omitempty"`
Decisions []string `json:"decisions,omitempty"`
Actions []string `json:"actions,omitempty"`
RelatedIDs []string `json:"relatedIds,omitempty"`
}
type Observation struct {
IndicatorID string `json:"indicatorId"`
Date string `json:"date"` // RFC 3339 date
Value *float64 `json:"value,omitempty"`
Level string `json:"level,omitempty"` // qualitative
Source string `json:"source,omitempty"`
EvidenceIDs []string `json:"evidenceIds,omitempty"`
Note string `json:"note,omitempty"`
Quality string `json:"quality,omitempty"` // verified | reported | estimated
PathwayID string `json:"pathwayId,omitempty"` // when one indicator is observed per pathway
Disaggregation map[string]string `json:"disaggregation,omitempty"`
}
type AssumptionAssessment struct {
AssumptionID string `json:"assumptionId"`
Date string `json:"date"`
Status string `json:"status"`
Rationale string `json:"rationale,omitempty"`
EvidenceIDs []string `json:"evidenceIds,omitempty"`
}
impact.Status (kind ImpactModelStatus) holds asOf, options
(weights / tolerance used), and per-element results: indicators[]
(progress, expected, status, health, latest observation), outcomes[]
(status, pathwayStatus, confidence, assumptionBroken[]), links[] (state,
confidence), assumptions[] (effective status, source), overall
(per impact outcome). Field-level schema is generated; this TRD fixes the
semantics above.
7.3 USM / UFM integration contract¶
- Identity: every ToC and element is addressable by §6.7 references.
- Perspective:
Perspective.actorIdidentifies the market participant (USM) andPerspective.futureRef(ufm:<future-slug>) groups ToCs by competing future (UFM). - Shared actors:
ActorRegistrydocuments (kind: ActorRegistry, a list ofActor) let several ToCs reference the same actors; actor IDs from a registry are resolved before INV-1/INV-4 checks. - Alignments: BSC objectives and measures attach via
Alignmentwith frameworkbsc; USM computes its scorecard from ToCStatusplus its own BSC data — the ToC never stores BSC data. - No back-references: a ToC never lists the USM/UFM models that consume it.
7.4 Data Storage¶
| Data Type | Storage | Rationale |
|---|---|---|
| ToC documents, observation sets, actor registries | Files in git (JSON or YAML) | Reviewable diffs, history, PR workflow; no DB in scope |
| Status snapshots | Files (JSON), optional commit per period | Enables Diff across periods |
| Schemas, template, rubric | Embedded in binary and committed | Offline use; single source |
8. Operational Guarantees¶
| Category | Requirement | Measurement | Applicability |
|---|---|---|---|
| Performance | Validate + lint + status + exec report for 200 outcomes / 400 links / 300 indicators / 5,000 observations in < 1 s on a laptop (Apple M-series) | go test -bench benchmarks |
Applicable |
| Throughput | Not applicable — single-user CLI/library | — | — |
| Availability | Not applicable — no service | — | — |
| Reliability | Deterministic outputs (INV-3) | Golden tests run twice | Applicable |
| Capacity limits | PathsToImpact bounded by max (default 1,000) |
Unit test | Applicable |
| Degraded behavior | Missing observations yield no_data/stale, never errors |
Unit tests | Applicable |
| Observability | Library returns errors; CLI logs via slog to stderr with --verbose |
Manual | Applicable |
| Auditability | Every reported status cites inputs (INV-5); git history is the audit trail | Report tests | Applicable |
9. Security and Privacy¶
- [x] Authentication mechanism — Not applicable: local CLI and stdio MCP; the MCP client process owns authentication.
- [x] Authorization model — MCP
pathinputs confined to--root(§6.3). - [x] Data encryption at rest — Not applicable: files under the user's control; no storage layer.
- [x] Data encryption in transit — Not applicable: no network I/O (INV-7).
- [x] Audit logging — git history plus revision log.
- [x] Vulnerability scanning — CodeQL (go-sast) and
govulncheckin CI; dependencies pinned ingo.sum.
Threat model highlights: (1) untrusted documents — parser limits (max file
size 10 MB, max 10,000 elements) prevent memory exhaustion; (2) user report
templates run text/template with no custom functions that touch the
filesystem or network; (3) restricted evidence never rendered (INV-6);
(4) MCP path traversal blocked; (5) personal data — the model is designed
for aggregate observations; stories identify actors by registry ID (which may
be an anonymized label), quotes require consent: granted, and documentation
directs users to keep personal records (contacts, sign-ups) in their source
systems and import only aggregates; (6) theme coding provenance is recorded
(codedBy) so model-coded stories can be audited; (7) external export
tools are invoked with fixed binary names and argument vectors (no shell),
on files the CLI itself wrote; (8) agent edits are confined to validated
patches (INV-9), so a misbehaving agent cannot leave a document invalid
without --allow-errors.
10. Compatibility and Migration Guarantees¶
Applicability: Applicable from the first tagged release.
- Supported versions:
specVersion0.1; loaders reject unknown majors. - Forward / backward compatibility: v0.x minors are additive; unknown
fields are rejected by schema (
additionalProperties: false) to catch typos, so a newer document fails on an older binary with a clear error. - Mixed-version deployment: Not applicable — no service.
- Migration phases: major bumps ship
impact-model migrateconverting the previous major. - Data validation: migrated documents must pass
impact-model validate. - Rollback boundary: documents are files in git; rollback is a revert.
11. Dependencies¶
| Dependency | Version (verified) | Purpose | Criticality |
|---|---|---|---|
| Go | 1.26+ | Language (org standard) | High |
github.com/grokify/prism-core |
v0.5.0 | Metadata, Person, Risk, health, trend, CapabilityRef | High |
github.com/grokify/prism-roadmap |
v0.21.0 | OKR / OutcomeJourney export (adapter only) | Medium |
github.com/plexusone/structured-evaluation |
v0.14.0 | summary.SummaryReport, rubric.RubricSet |
High |
github.com/invopop/jsonschema |
v0.14.0 | Schema generation | High |
github.com/santhosh-tekuri/jsonschema/v6 |
v6.0.3 (same as prism-maturity) | Schema validation | High |
sigs.k8s.io/yaml |
latest verified at implementation time | YAML → JSON | Medium |
github.com/spf13/cobra |
v1.10.2 | CLI | Medium |
github.com/modelcontextprotocol/go-sdk |
latest verified at implementation time (≥ v1.8.0) | MCP server | Medium |
schemakit (tool) |
installed | Schema lint in CI | Medium |
@marp-team/marp-cli (optional external) |
latest at use | Deck export to PDF / PPTX / HTML | Low — Markdown deck works without it |
@mermaid-js/mermaid-cli mmdc (optional external) |
latest at use | Diagram → SVG for decks, PNG for the pandoc profile |
Low — Mermaid source works without it |
pandoc (optional external) |
3.x at use | Markdown → DOCX / PPTX for sharing | Low — Markdown export works without it |
Google Drive tooling (external, used only by the toc-share-google skill) |
gogoogle CLI with driveutil (from INIT-GOGOOGLE-001) and mcp-google comments/suggestions tools (from INIT-MCPGOOGLE-001) |
Upload with conversion; read comments, suggestions, Sheets | Low — manual upload and CSV review sheet are the fallback |
JSON Patch is implemented in-house in pkg/edit (RFC 6902 plus ID
addressing); see §14.
Versions are re-verified with go list -m -versions when each dependency is
first added (global dependency policy). Fallback: if prism-roadmap's export
types change, pkg/prismx is the only package affected.
12. Testing Strategy¶
- Unit tests per package, table-driven;
t.Fatalon errors. - Golden files in
testdata/for lint reports, status snapshots, and reports;-updateflag regenerates. - Seeded-defect corpus: one fixture per lint rule.
- Property tests: random DAGs for graph invariants (paths end at impact, levels monotone).
- Import-boundary test (TRD-014).
- Schema drift test (TRD-003) and embedded-schema load test.
- MCP in-memory transport test (TRD-013).
- Benchmarks for §8 performance.
- Coverage ≥ 80 % on
pkg/....
13. Deployment¶
13.1 Rollout and Feature Flags¶
Not applicable — library and CLI released as tagged Go module versions
(go install …/cmd/[email protected]). Releases follow the org release process
(CHANGELOG.json via structured-changelog; tag after CI is green).
13.2 Rollback Plan¶
Consumers pin module versions; rollback is pinning the previous tag. Published tags are immutable, so fixes roll forward.
14. Alternatives Considered¶
| Alternative | Why rejected |
|---|---|
| Hand-written JSON Schema as the source of truth | Violates org Go-first policy; drifts from code |
| Embed observations inside the ToC document | Couples slow-changing theory with per-period data; noisy diffs; harder agent edits. Rejected for separate ObservationSet |
| Model the ToC as a logframe (fixed input → activity → output → outcome → impact rows) | Cannot express multiple pathways, AND/OR preconditions, or feedback; loses the ToC's main value |
Adjacency stored on outcomes (preconditions: [ids]) instead of first-class links |
Links need their own rationale, assumptions, evidence, and confidence; first-class edges keep those addressable |
Reuse prism-roadmap common.Assumption |
Lacks type, criticality, appliesTo, test, and status needed for propagation; ToC-specific type is justified, and prism-core has no assumption type |
Wrap structured-evaluation claims.Claim for evidence (as prism-roadmap assessment does) |
Heavier than needed for v0; revisit when evidence verification by LLM judges is in scope |
| Graph database or Dolt storage | Out of scope; git files suffice for document-sized graphs |
Per-outcome logic only (no groups) |
Cannot express nested logic such as (A AND B) OR C; ActKnowledge "clusters" and Davies' critique both call for explicit joint preconditions |
| Interventions only as nodes with outgoing links (logic-model style) | ActKnowledge places interventions on connectors ("what it takes for A to produce B"); both placements are supported via Intervention.targets and CausalLink.interventionIds |
| Agents rewrite whole documents (no patch engine) | Large, unreviewable diffs; easy to drop elements silently; no atomic validation. Patches are small, reviewable, and checkable before write |
| Third-party JSON Patch library (e.g. evanphx/json-patch) | Lacks ID-segment addressing and error-delta rejection; wrapping it still needs a custom resolver; the in-house engine over impact.Model JSON is small and fully tested |
| Generate PPTX directly from Go | Large dependency and layout effort; Marp gives Markdown-native decks consistent with prism-maturity, with PDF/PPTX export as an optional step |
| Import Google Drive SDK into this module to publish and read reviews | Couples a spec library to one cloud; violates INV-7; the file-based ReviewResponse lets any channel (Google, Microsoft 365, email, workshop notes) feed the same import |
| Apply review responses directly | Free-text feedback needs judgment; proposals plus impact-model patch keep a human decision on every change and keep INV-9 |
| Marp PPTX for shared decks | Marp's PPTX is image-based, so Google Slides conversion yields non-editable, unreadable slides; Pandoc PPTX keeps native text |
| Judge inside this module via an LLM SDK | Violates the no-LLM-dependency non-goal; bundle + verdict files let any judge (agent, visionspec, visionstudio) participate and keep results auditable |
| Closed enum for outcome category | Development (ORS, COM-B), market-systems (AAER), and corporate ToCs need different taxonomies; category is an open string with documented recommended vocabularies |
14.1 Deferred extensions (reserved names)¶
From the research brief §8.4, these are deferred to a later initiative and
their names are reserved so v0 documents do not collide:
contributionClaims (Contribution Analysis), observedOutcomes (Outcome
Harvesting with substantiation; an unmapped observed outcome becomes a
revision prompt — Most-Significant-Change-style stories are in scope via
ObservationSet.stories), and logframe matrix projections (per-pathway
logic-model views are in scope). The UNDG problem tree is in scope as
Model.causes (research §9.8).
15. Risks and Open Questions¶
| ID | Risk / Question | Impact | Mitigation / Resolution |
|---|---|---|---|
| Q-1 | Should output be an outcome level or a separate type? |
Model shape | Resolved: outcome level output, so outputs can be measured and linked uniformly; interventions also list free-text outputs |
| Q-2 | Default roll-up worst may look pessimistic to executives |
Adoption | Configurable; exec report states the method |
| Q-3 | Confidence weights are opinionated | Trust | Defaults documented, overridable, printed with every score |
| R-1 | schemakit may flag map[string]string disaggregation |
Lint failure | Replace with []KeyValue if flagged |
| R-2 | prism-core Metadata uses omitzero time fields; schema generation of time.Time |
Schema shape | Accept date-time format; covered by schema tests |
No open question blocks implementation.
Appendix¶
A. Revision History¶
| Version | Date | Author | Changes |
|---|---|---|---|
| 0.1 | 2026-10-29 | John Wang | Initial draft |
| 0.2 | 2026-10-29 | John Wang | Measurement-framework design for small-staff and intermediary organizations: pathways, metric library, claims, selection, burden, audiences, glossary, story codebook, learning reviews, CSV interop, definition reports, agent skills |
| 0.3 | 2026-10-29 | John Wang | UNDG primary-source review: problem tree (causes), outcome responsibility and delivering actors, focus / solution prioritization criteria, target groups, consultation records, partnership assumptions with plausibility and needs-testing, risk categories, narrative and division-of-labour reports, core + development rubric profiles |
| 0.4 | 2026-10-29 | John Wang | Editing (patch engine with ID addressing, element commands, canonical fmt, semantic diff, impact_patch), presentations (Marp decks, optional SVG/PDF/PPTX export), judging (bundles, verdict files, multi-judge agreement), lint profiles |
| 0.5 | 2026-10-29 | John Wang | Public-source drafting with element provenance and data availability, assessment report, sharing exports (pandoc profile, review packet and sheet, export stamps), ReviewResponse import as proposals, approval, impact-draft-from-sources and toc-share-google skills over external Google tooling |
| 0.6 | 2026-11-01 | John Wang | Problem trees: causes become a network (parents[]), effects (branches) with addressesEffects, lint IMP-EFF-001/002, and the problem-tree reports built from a JSON diagram spec (TRD-043) |