Skip to content

Cross-spec decision propagation: a post-MVP backend decision silently diverged a later consumer spec's contract — the system view should link them #304

Description

@montfort

Summary

A real adopter case (Sentinel, dogfooding StrayMark) surfaced a class of drift StrayMark does not currently catch: a decision recorded in one spec's post-MVP backlog silently invalidated the contract assumed by a later, separate spec — and nothing connected the two. The implementation then diverged from both, and the tests passed because their mocks encoded the assumed (wrong) contract.

This is adjacent to #303 (cross-boundary contract verification at audit time) but is a different lever: it is about decision propagation across specs and the system view that should make a contract's source-of-truth singular. Filing it as a capability gap for the Loom / architecture model and the cross-artifact analysis.

The concrete case

Sentinel has two SpecKit specs authored months apart:

  • spec 001 (backend MVP) modeled service health as service-level (score + state). Its post-MVP backlog item PM002 (later approved, AILOG-2026-04-24-006) extended the model to per-component health (score + state per component) and per-service metrics persisted in a versioned health_scores.details JSONB.
  • spec 005 (frontend dashboard), authored later, defined the Health Dashboard (FR-010: "estado por componente y métricas (latencia P95, error rate, CPU, memoria)").

Three artifacts that should agree drifted into a triple mismatch:

  1. Frontend implementation assumed a contract that matched neither — wrong field names (name vs display_name, status vs state, score vs health_score), wrong enum values (GREEN/YELLOW/RED vs OPERATIONAL/DEGRADED/MAJOR_OUTAGE/IDLE), and per-component raw metrics that were never modeled (it rendered latency/CPU/memory columns per component).
  2. spec 005 FR-010 was ambiguous about granularity ("estado por componente" and "métricas" — two clauses the implementation fused into "metrics per component").
  3. spec 001/PM002 — the actual source of truth for the backend contract — was never referenced by spec 005, because it lived in a different spec's post-MVP backlog. The frontend author had no thread pointing to it.

Net effect: the frontend's Health view never worked against real data. It only "passed" because e2e/unit mocks reproduced the assumed contract. The drift was invisible until the SPA ran against the real backend in staging (a runtime TypeError: t.find is not a function).

Why nothing caught it

  • The truth was distributed across 3 documents with no edges between them. The contract producer (backend handler), the decision that shaped it (PM002, in another spec's backlog), and the consumer (frontend spec + code) were not linked in any StrayMark artifact.
  • A post-MVP decision in spec A is invisible to spec B. PM002 changed what spec 001 promised, but spec 005 was authored against spec 001's original shape. Nothing flagged "a consumer spec depends on a contract that a later decision changed."
  • Mocks encoded the assumption, not the contract. Green tests gave false confidence — they verified the code against the same wrong model it was built on.

The generalizable pattern

A decision (AILOG/AIDEC/PM/Charter) that changes a contract in one spec does not propagate to downstream specs/components that consume that contract, and no view makes the contract's source-of-truth singular. The drift only manifests at runtime, at the consumer boundary.

This is the SpecKit↔StrayMark gap in miniature: SpecKit decisions are per-spec and time-sliced; the system they describe is one graph. StrayMark's promise is to be that graph ("the cognitive discipline … so AI agents stay coherent across many turns"), but today it doesn't link a decision to the cross-spec contracts it invalidates.

Proposal (capability directions)

  1. Cross-spec contract provenance / dependency edges. Let a spec (or component) declare "consumes contract X produced by component/spec Y." Then a decision that touches Y's contract can surface "N downstream consumers may be affected," including consumers in other specs and in post-MVP backlogs.
  2. Decision → contract → consumer linking in the system view (Loom / architecture model). When a post-MVP/PM decision changes a contract, the model should connect it to every consumer so "where is the truth for this contract?" has one answer, not three. A consumer built against a stale shape would show as a dangling/aged edge.
  3. Drift signal at authoring time, not just audit time. Audit-prompt: add cross-boundary contract verification (client-consumes-server in monorepo) #303 adds cross-boundary verification to the audit prompt (after the fact). This asks for the same coherence to be a structural property of the model — so the gap is visible while authoring spec 005, not discovered in staging.
  4. "Mock encodes assumption" smell. Optional/longer-term: flag tests whose fixtures assert a contract shape that no producing handler/schema corroborates (the false-green amplifier here).

Evidence (adopter: Sentinel)

  • Remediation of the runtime crash + full bidirectional contract audit: AILOG-2026-06-24-001 (Sentinel), follow-ups FU-005-005 / FU-005-006.
  • Backend source of truth: internal/modules/statuscenter/handler.go (getServiceHealthOutput, componentResponse), health_details.go (JSONB v2: metrics + components), interfaces/status.go (HealthState enum).
  • The decision that drifted: AILOG-2026-04-24-006 (PM002, per-component health), in spec 001's post-MVP backlog — never referenced by spec 005's FR-010.

Relation to existing issues


Filed from real adopter remediation (Sentinel). The triple-mismatch and "mock encodes the assumption" pattern were the load-bearing lessons.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions