Skip to content

INV-3: changereportcategory Vocabulary Archaeology

INV-3: change_report_category Vocabulary Archaeology

Section titled “INV-3: change_report_category Vocabulary Archaeology”

Date: 14/05/2026 · Wave: S236 WP6 INV-3 · Author: INV-3 sub-agent Branch: content-items-investigation · Source under question: docs/plans/phase-0-investigation/phase-b-prerequisite-1-onthology-pipeline.md §2.1, row change_report_category (NEEDED).


No expanded vocabulary located. The 4 starter values in the source row (content_added, content_edited, content_promoted, content_archived) appear ONLY in the source phase-b-prerequisite-1-onthology-pipeline.md itself and the derived docs/ontology/28-change-report-category.md. There is no other authoritative document enumerating a fuller set, no migration, and no Liam ratification beyond ONT.15 (RESOLVED-S234 — closed list ships with digest → change_reports rename).

Critical archaeology finding: the live, shipped MCP tool get_change_report (P1-35, in lib/mcp/tools/change-report.ts) operates on a 3-bucket schemeadditions / updates / removals — not the 4-value scheme in the ontology MD. These are the two parallel realities of “change report category” today. INV-3 cannot reconcile without Liam’s ruling: are the four ontology values the ratified target vocabulary (event-level taxonomy for the future change_reports table), and is the existing 3-bucket tool simply a different surface (a runtime aggregation grouped by event type)? Or should the ontology vocabulary collapse to match the implemented 3-bucket scheme?

Recommended C1 action (preferred): ratify the existing 4 values + close the list, because the change_reports table does not yet exist and these four match the dominant verbs in the digest/change-report code surface (created → added; updated → edited; archived → archived; the new promoted category captures publication-state transitions per the §5.2 publication lifecycle). Status planned, awaiting combined-PR Q-OQR1-16 schema. Notes section flags the 3-bucket tool divergence for ratifier wave.


#Tool / locationQueryResult
1grepchange_report_category across docs/ scripts/ lib/ types/ supabase/ app/7 hits — all in 4 ontology/plan documents (no specs, no code).
2grepdigest_category across same paths0 hits. The concept is not surfaced as a column or constant under that name.
3grepcontent_added|content_edited|content_promoted|content_archived6 hits — all in 28-change-report-category.md and the source plan §2.1 row. Zero hits in code.
4grep”change report” / “change_report” across docs/30+ hits across plans, specs, audits, ontology — none enumerating a vocabulary beyond the source row.
5grep”change report” / “change_report” across lib/ types/ app/ components/ supabase/Concentrated in lib/mcp/tools/change-report.ts (P1-35), lib/mcp/formatters/change-report.ts, lib/digest/digest-export.ts, app/digest/, lib/validation/schemas.ts (email_weekly_change_report, auto_generate_change_reports notification prefs), app/api/notifications/preferences/route.ts. No vocabulary enum.
6grepchange_type enum live values (CHECK constraint)content_history.change_type has 11 values: create, edit, ai_update, import, merge, rollback, archive, delete, metadata_change, owner_change, publication_state (per migration 20260427164439_extend_content_history_change_type_for_publication_state.sql). This is a different but adjacent vocabulary (history events on a single content_item, not aggregated change-report categories).
7mcp__supabase__execute_sqldigests table schema15 columns; no category column. Only digest_type (weekly, daily, custom — cadence, not category). 2 prod rows, both weekly.
8mcp__supabase__execute_sqltables matching %change% or %digest%Only digests exists. No change_reports table yet — confirms ONT.15 “ships with digest → change_reports rename” hasn’t landed.
9Readlib/mcp/tools/change-report.ts (full file)Authoritative live code: P1-35 get_change_report MCP tool uses 3-bucket scheme: additions (newly created in window), updates (modified in window but pre-existing), removals (archived in window). Each bucket queried independently from content_items by created_at / updated_at / archived_at timestamps. No category column read or written anywhere.
10Readlib/mcp/formatters/change-report.tsConfirms ChangeReportData TypeScript interface uses additions: { count, items }, updates: { count, items }, removals: { count, items }. No category enum.
11Readdocs/specs/p1-35-get-change-report-spec.md (full spec)Confirms 3-bucket design ratified at P1-35 implementation time. “Open questions: None” — this was a settled design. No mention of a change_report_category ontology CV.
12grepdocs/specs/ for change_report_category / change-report-categoryOnly INV-3 sibling files (verifier reports). No spec enumerates the vocabulary.
13Readdocs/plans/phase-0-investigation/0.9-decision-graph.md ONT.15 rowRESOLVED-S234 — closed list ships with digest → change_reports rename”. Closed-CORE ratification, but no list expansion.
14grepphase-b-prerequisite-1-onthology-pipeline-feedback-investigation.md for change_report_categoryConfirms `CORE (closed)
15mempalace_search”change_report_category vocabulary content_promoted Liam ratification”0 relevant hits — no Liam ratification of expanded vocabulary in mempalace.
16mempalace_search”digest category additions updates removals enum”0 relevant hits.
17find.planning/.archive/.specs/ for digest/change-reportOne archived spec found: digest-detail-route-spec.md (UI/route spec, not vocabulary).

Bottom line: every documented mention of change_report_category traces back to the source row in §2.1, which itself ends in “etc.” The 4 values are the only candidates that exist anywhere in the corpus. The actual implemented get_change_report tool uses a separate 3-bucket scheme that has no vocabulary enum (the buckets are computed from timestamp filters, not stored as a column).


3.1 Primary source — phase-b-prerequisite-1-onthology-pipeline.md §2.1

Section titled “3.1 Primary source — phase-b-prerequisite-1-onthology-pipeline.md §2.1”

docs/plans/phase-0-investigation/phase-b-prerequisite-1-onthology-pipeline.md:71 (verbatim):

| `change_report_category` (NEEDED — not built) | new vocabulary | content_added, content_edited, content_promoted, content_archived, etc. | Change reports (digest rename) needs typed vocabulary |

The etc. is unresolved. This is the only enumeration in the corpus.

3.2 Decision-graph status — 0.9-decision-graph.md ONT.15

Section titled “3.2 Decision-graph status — 0.9-decision-graph.md ONT.15”

docs/plans/phase-0-investigation/0.9-decision-graph.md:455 (verbatim):

| ONT.15 | `change_report_category` Layer-1 CV | **RESOLVED-S234** — closed list ships with digest → change_reports rename | Onto-doc §2.1 |

This locks classification (CLOSED-CORE) but doesn’t expand the list.

3.3 Feedback investigation — phase-b-prerequisite-1-onthology-pipeline-feedback-investigation.md

Section titled “3.3 Feedback investigation — phase-b-prerequisite-1-onthology-pipeline-feedback-investigation.md”

docs/plans/phase-0-investigation/phase-b-prerequisite-1-onthology-pipeline-feedback-investigation.md:663 (verbatim):

| `change_report_category` (NEEDED) | **CORE (closed)** | Platform vocab. | CORE. |

Confirms CORE-closed classification, no list.

3.4 Existing ontology MD — docs/ontology/28-change-report-category.md

Section titled “3.4 Existing ontology MD — docs/ontology/28-change-report-category.md”

Already contains the 4 starter values verbatim (drafter A1 transcribed from §2.1, which is correct). Status: needed. Notes line 46 explicitly flags: “Source §2.1 lists ‘etc.’ after these — verifier sub-agent should enumerate the full set or flag as needs-Liam-input.”

3.5 Combined-PR scope — 0.9-decision-graph.md §11.3 item 7

Section titled “3.5 Combined-PR scope — 0.9-decision-graph.md §11.3 item 7”

docs/plans/phase-0-investigation/0.9-decision-graph.md:486:

7. Digests rename: `digests` → `change_reports` (Q5.x); cron + UI text updates

Confirms the rename lands in the combined PR (Q-OQR1-16) but does NOT prescribe a category column or vocabulary.


4. Full Vocabulary List (with Definitions and Provenance)

Section titled “4. Full Vocabulary List (with Definitions and Provenance)”

Based on the only authoritative enumeration (§2.1) and code-side reality:

#KeyLabelProvenanceDefinition (proposed by INV-3)Source
1content_addedContent AddedcoreA new content_items row was created within the change-report window (matches get_change_report “additions” bucket: created_at >= cutoff AND archived_at IS NULL).phase-b-prerequisite-1-onthology-pipeline.md:71 + maps to lib/mcp/tools/change-report.ts:96-110
2content_editedContent EditedcoreAn existing content_items row was modified within the window (matches “updates” bucket: updated_at >= cutoff AND created_at < cutoff AND archived_at IS NULL). Maps to content_history.change_type IN ('edit', 'ai_update', 'metadata_change') events.phase-b-prerequisite-1-onthology-pipeline.md:71 + maps to lib/mcp/tools/change-report.ts:113-127
3content_promotedContent PromotedcoreA content_items row transitioned publication_status from draft or in_review to published within the window. Maps to content_history.change_type='publication_state' (added in migration 20260427164439). No equivalent bucket in the live get_change_report tool today — the existing tool predates the publication-lifecycle work and conflates this into “updates”.phase-b-prerequisite-1-onthology-pipeline.md:71 + inferred from §5.2 publication-lifecycle spec (docs/specs/publication-lifecycle-state-machine-spec.md)
4content_archivedContent ArchivedcoreA content_items row had archived_at set within the window (matches “removals” bucket: archived_at >= cutoff). Maps to content_history.change_type IN ('archive', 'delete').phase-b-prerequisite-1-onthology-pipeline.md:71 + maps to lib/mcp/tools/change-report.ts:130-142

NONE. No additional values located in any authoritative source. The “etc.” in §2.1 was speculative shorthand; no follow-up document expanded it.

Adjacency note — content_history.change_type (CV 11) is NOT this vocabulary

Section titled “Adjacency note — content_history.change_type (CV 11) is NOT this vocabulary”

content_history.change_type is the per-event audit-row type (one row per state change on one content_item). Live values: create, edit, ai_update, import, merge, rollback, archive, delete, metadata_change, owner_change, publication_state. The ontology’s CV 11 covers this column.

change_report_category (CV 28) is one cardinality up — it categorises Change Report rows themselves (a Change Report aggregates many content_history events for a domain/window). Mapping above shows how the 4 categories collapse the 11 history-event types into a user-facing 4-bucket taxonomy.


There is no TS const tuple, Zod enum, or interface field that names the 4 categories. The closest analogues are:

  • ChangeReportData interface (lib/mcp/formatters/change-report.ts:23-32):

    export interface ChangeReportData {
    period_days: number;
    start_date: string;
    end_date: string;
    domain: string | null;
    keywords: string[] | null;
    additions: { count: number; items: ChangeReportItem[] };
    updates: { count: number; items: ChangeReportItem[] };
    removals: { count: number; items: ChangeReportItem[] };
    }

    This is a 3-bucket scheme with bucket names hardcoded as object keys, not enum values.

  • digestTypeLabel (lib/digest/digest-helpers.ts:1-13): switches on weekly / daily / custom — that’s digest_type (cadence), not change_report_category.

  • types/digest.ts has Digest / DigestDomainSummary / DigestGovernanceSummary interfaces — none carry a category enum.

mcp__supabase__execute_sql on prod project rovrymhhffssilaftdwd:

  • digests table exists with 15 columns (id, digest_type, period_start, period_end, item_count, domain_summaries, theme_clusters, narrative_summary, generated_at, generated_by, tokens_used, metadata, created_at, item_ids, created_by). No category column.
  • 2 prod rows; both digest_type='weekly'.
  • No change_reports table — confirms the rename hasn’t landed.

The live, shipped tool (P1-35, lib/mcp/tools/change-report.ts) categorises by timestamp filters, not by a stored category column:

  • Additions = created_at >= cutoff AND archived_at IS NULL (would map to content_added).
  • Updates = updated_at >= cutoff AND created_at < cutoff AND archived_at IS NULL (would map to content_edited; conflates content_promoted since publication_state transitions also bump updated_at).
  • Removals = archived_at >= cutoff (would map to content_archived).

No content_promoted bucket exists in the tool today. Promotions show up as updates.


Section titled “6. Recommended C1 Action for 28-change-report-category.md”
Section titled “Recommended action: PROCEED with the 4 starter values; replace “etc.” narrative with explicit closed-list framing.”
  1. The 4 values map cleanly onto the existing content_history.change_type enum and the get_change_report tool’s 3-bucket scheme + the publication-state event added in 20260427164439. They cover the full change-event surface area today.
  2. ONT.15 ratification (RESOLVED-S234) explicitly says “closed list ships with digest → change_reports rename”. A closed list with 4 values is a sufficient first cut; the combined PR Q-OQR1-16 lands the schema where change_reports.category will be added.
  3. No alternative or additional values exist anywhere in the corpus. Asking Liam to confirm the 4 (rather than asking him to draft net-new vocabulary) is the lower-friction path.
  4. The 3-bucket vs 4-value divergence with the live tool is reconcilable: the tool’s 3 buckets are a runtime aggregation of change_reports rows by their category. A future refactor of get_change_report to read from change_reports.category would naturally split “updates” into “edited” + “promoted” — a quality-of-life improvement, not a breaking change.
  1. Frontmatter status: — change from neededplanned (consistent with other CVs whose target column doesn’t exist yet, e.g. CV 20 chunk-kind, CV 22 origin-kind).

  2. §Definition — keep current prose; add one sentence at the end:

    Closed enumeration of 4 values, ratified S234 (ONT.15) as CORE-closed. No further values planned for v1; future event types may be added in a CHECK-extending migration.

  3. §Baseline values — keep the 4 rows as they are; remove line 46 (“Source §2.1 lists ‘etc.’ after these — verifier sub-agent should enumerate the full set or flag as needs-Liam-input.”) since INV-3 has resolved this.

  4. §Notes — add the following bullets:

    • Provenance: Source §2.1 (the only enumeration in the corpus) lists 4 values + “etc.” Archaeology (S236 INV-3) found no additional values across docs/, lib/, types/, supabase/migrations/, .planning/.archive/, mempalace, or the live DB. Recommendation: ratify the 4 + close the list. No further Liam input required unless he wishes to add a 5th category at this stage.
    • Code-side divergence: The live get_change_report MCP tool (lib/mcp/tools/change-report.ts, P1-35) categorises by 3 timestamp buckets (additions/updates/removals) computed at query time, NOT by reading a category column. When change_reports.category lands in the combined PR Q-OQR1-16, the tool can be refactored to split “updates” into content_edited + content_promoted (publication-state transitions). This is a quality-of-life improvement, not a breaking change.
    • Mapping to content_history.change_type (CV 11): the 4 categories aggregate the 11 per-event types as follows: content_addedcreate/import; content_editededit/ai_update/metadata_change/owner_change; content_promotedpublication_state (only when transitioning to published); content_archivedarchive/delete. merge and rollback may map to content_edited or be excluded from change reports entirely (open question for ratifier wave).
  5. §Relationships — keep current prose; tighten the line about change_type (CV 11) to reflect the explicit aggregation mapping in the §Notes bullet above.

Open items (low priority, not blocking ratification)

Section titled “Open items (low priority, not blocking ratification)”
  • (a) Should merge and rollback change_type events surface in change reports at all? They’re administrative/recovery operations; arguably not user-relevant. Recommendation: exclude from v1; revisit if user feedback requests.
  • (b) Should content_promoted also fire on published → archived (deprecation event), or only on transitions INTO published? Recommendation: only INTO published. Deprecation should map to content_archived since archived_at is set at the same time.

ClaimEvidence
4 values are the only enumeration in the corpusgrep across docs/ scripts/ lib/ types/ supabase/ app/ returned 6 hits, all in 28-change-report-category.md + source §2.1 row.
ONT.15 ratifies “closed list” classificationdocs/plans/phase-0-investigation/0.9-decision-graph.md:455 verbatim.
Feedback investigation confirms CORE-closedphase-b-prerequisite-1-onthology-pipeline-feedback-investigation.md:663 verbatim.
change_reports table doesn’t exist yetmcp__supabase__execute_sql confirms only digests table exists.
digests has no category columnmcp__supabase__execute_sql on information_schema.columns confirms 15 columns, no category.
Live tool uses 3 timestamp buckets, not 4 categorieslib/mcp/tools/change-report.ts:96-142 and lib/mcp/formatters/change-report.ts:23-32.
No TS enum exists for the 4 categoriesgrep across lib/ types/ returned zero matches for any of the 4 values.
content_history.change_type is the per-event vocabulary (CV 11), not this CVMigration 20260427164439_extend_content_history_change_type_for_publication_state.sql enumerates 11 values; docs/ontology/28-change-report-category.md notes the one-to-many adjacency.
Mempalace has no Liam ratification of expanded vocabularymempalace_search queries returned 0 relevant hits across 15 candidates each.

-- digests table schema (no category column)
SELECT column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_name = 'digests' AND table_schema = 'public'
ORDER BY ordinal_position;
-- digest_type live values (cadence, not category)
SELECT digest_type, COUNT(*) FROM digests GROUP BY digest_type ORDER BY count DESC;
-- Confirm change_reports table doesn't exist yet
SELECT table_name FROM information_schema.tables
WHERE table_schema = 'public'
AND (table_name LIKE '%change%' OR table_name LIKE '%digest%')
ORDER BY table_name;
-- Adjacent CV 11 — content_history.change_type live CHECK enum (11 values post-migration 20260427164439)
SELECT pg_get_constraintdef(oid)
FROM pg_constraint
WHERE conname = 'content_history_change_type_check';

Appendix A — Files Verified, Not Modified

Section titled “Appendix A — Files Verified, Not Modified”

This investigation reads the following but does not edit any of them:

  • docs/ontology/28-change-report-category.md
  • docs/ontology/README.md
  • docs/plans/phase-0-investigation/phase-b-prerequisite-1-onthology-pipeline.md
  • docs/plans/phase-0-investigation/0.9-decision-graph.md
  • docs/plans/phase-0-investigation/phase-b-prerequisite-1-onthology-pipeline-feedback-investigation.md
  • docs/specs/p1-35-get-change-report-spec.md
  • lib/mcp/tools/change-report.ts
  • lib/mcp/formatters/change-report.ts
  • lib/digest/digest-helpers.ts
  • lib/digest/digest-export.ts
  • types/digest.ts
  • supabase/migrations/20260427164439_extend_content_history_change_type_for_publication_state.sql

Per agent prompt §Constraints: “Do NOT touch any path outside docs/specs/wp6-ontology-harness/investigations/.”