Skip to content

Release Notes: v0.2.0

Release Date: 2026-07-25

Highlights

  • Product Signal Types: 5 new signal.Type values for capturing product and market intelligence alongside operational signals
  • Cross-Repo References: pkg/ref package with TypedRef for linking entities across repository boundaries
  • Fingerprinting: Deterministic ComputeFingerprint() for signal deduplication
  • Embedded Schemas: schema/ package exposes JSON schemas at runtime via go:embed
  • Derived Metrics: DerivedMetrics struct for computed scores kept separate from signal identity

What's New

Product Signal Types

Signal Spec now covers product and market intelligence in addition to operational observations. Five new signal.Type values are available:

import "github.com/plexusone/signal-spec/pkg/signal"

sig := signal.Signal{
    ID:   "sig-2026-005678",
    Type: signal.TypeEnhancementRequest,
    // ...
}
  • enhancement_request - Customer feature requests with vote/subscriber metadata
  • competitive_gap - Gaps vs. competitors identified from win/loss analysis
  • competitor_launch - Competitor product announcements
  • analyst_finding - Insights extracted from analyst reports
  • market_observation - General market trends

Enhancement Signal Metadata Conventions

enhancement_request signals carry structured product data via well-known Metadata keys. All keys are optional; adapters populate whichever keys their source system provides:

Constant Metadata Key Type Description
signal.MetaVotes votes int Total vote/upvote count
signal.MetaSubscribers subscribers int Number of watchers/subscribers
signal.MetaOrganizations organizations []string Requesting organization names
signal.MetaCustomers customers []string Named customer identifiers
signal.MetaOpportunities opportunities []string Sales opportunity IDs linked to this request
signal.MetaEstimatedARR estimated_arr int64 Estimated ARR at stake, in cents

Cross-Repo References (pkg/ref)

The new pkg/ref package defines TypedRef, a {type}:{slug} reference format for linking signal-spec entities to definitions owned by other repositories (e.g., MarketSpec, OrganizationSpec):

import "github.com/plexusone/signal-spec/pkg/ref"

r := ref.New(ref.TypeMarket, "identity-governance")
// r == "market:identity-governance"

if err := ref.ValidateStrict(r); err != nil {
    // handle invalid ref
}

Known reference types: customer, capability, market, competitor, analyst-report

pkg/ref provides:

  • New(type, slug) - construct a TypedRef
  • Parse(ref) - split into type and slug
  • Validate(ref) - check format and known type
  • ValidateSlug(slug) - check slug is lowercase alphanumeric/hyphen
  • ValidateStrict(ref) - format + known type + slug validity

Signals also carry cross-repo references via well-known Metadata keys:

Constant Metadata Key Example
signal.MetaCustomerRef customer_ref customer:acme-001
signal.MetaCapabilityRef capability_ref capability:sso
signal.MetaMarketRef market_ref market:identity-governance
signal.MetaCompetitorRef competitor_ref competitor:okta
signal.MetaAnalystReportRef analyst_report_ref analyst-report:gartner-mq-iam-2026

Entity.Ref

common.Entity now has an optional Ref field carrying a typed cross-repo reference, so entities referenced by a signal can be linked directly to their canonical definition:

entity := common.Entity{
    Type: "customer",
    Name: "Acme Corp",
    Ref:  "customer:acme-001",
}

Signal Fingerprinting

signal.ComputeFingerprint() returns a deterministic SHA-256 hex digest computed from a signal's identity fields (ID, Type, Source, Domain, Severity, Summary, Description, Entities, ObservedAt, Metadata, Tags). Mutable and derived fields — Status, Derived, Embedding, ReceivedAt, RootCauseID, and Fingerprint itself — are excluded, so the same raw input always produces the same fingerprint regardless of processing state:

fp, err := signal.ComputeFingerprint(sig)
if err != nil {
    // handle error
}
sig.Fingerprint = fp

Use fingerprints to deduplicate signals ingested from the same underlying event across multiple adapters or retries.

DerivedMetrics

The new DerivedMetrics struct holds computed scores that are recomputed over time and explicitly excluded from fingerprinting:

sig.Derived = &signal.DerivedMetrics{
    Frustration: ptr(4.2),
    Momentum:    ptr(1.8),
    Reach:       ptr(12),
    Urgency:     ptr(3.5),
}
  • Frustration - weighted signal count multiplied by age
  • Momentum - trailing signal count over a rolling window (e.g., 30 days)
  • Reach - count of distinct customer references contributing to a root cause
  • Urgency - case count weighted by severity
  • ComputedAt - when these metrics were last computed
  • Extra - additional derived scores not covered by the well-known fields

Embedded Schemas (schema/)

The new schema/ package embeds generated JSON schemas via go:embed, so consumers can validate signals at runtime without reading files from disk:

import "github.com/plexusone/signal-spec/schema"

// schema.SignalSchema, schema.RootCauseSchema, schema.RemediationSchema,
// and schema.ValidationSignalSchema are []byte
var s map[string]any
json.Unmarshal(schema.SignalSchema, &s)

// schema.All is an embed.FS containing all *.schema.json files

signal.schema.json and rootcause.schema.json were regenerated to include the new signal types, metadata conventions, and DerivedMetrics.

Installation

go get github.com/plexusone/signal-spec@v0.2.0

Migration Guide

From v0.1.0

No breaking changes. All v0.2.0 additions are purely additive:

  • Existing Signal, RootCause, Remediation, and ValidationSignal types are unchanged.
  • common.Entity.Ref is a new optional field; existing entity literals compile unchanged.
  • signal.Fingerprint was already a field on Signal; ComputeFingerprint() is a new helper to populate it.
  • Regenerate schemas with signal-spec schema generate -o schema/ (or import schema.SignalSchema directly) if you consume the JSON schema files.

Full Changelog

See CHANGELOG.md for the complete list of changes.