Generate contract types, pin fixtures to spec, add build and ADRs
Some checks failed
ci / build (push) Failing after 1m6s
Some checks failed
ci / build (push) Failing after 1m6s
Completes FLUID-WP-0002. The record types in internal/contract are generated from schemas/ by a dependency-free generator; fixtures transcribed from the spec's worked examples validate against those schemas and round-trip through the generated types with DisallowUnknownFields, so a spec change that misses the schemas fails CI rather than drifting silently. Adds identifier prefix helpers, Makefile, GitHub Actions, and five ADRs recording the Go choice, out-of-process attachment, the wire contract as boundary, the evidence store, and revision identity. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014KmVxhJ35tCo7rE7UnLwWu Assistant: claude-code Assistant-Model: opus Assistant-Process: 1116572@bnt-lap001 Assistant-Session: 8ba9bb93-a72a-4883-b189-2499cce5c400
This commit is contained in:
parent
fbbf56df7a
commit
76912adef8
44 changed files with 4010 additions and 7 deletions
91
internal/contract/ids.go
Normal file
91
internal/contract/ids.go
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
package contract
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Identifier prefixes from FluidHypothesisRevisionSchema.md section 16.
|
||||
//
|
||||
// Implementations may use UUIDs internally; these human-readable prefixes exist
|
||||
// so that operational tooling and audit trails stay legible to people.
|
||||
const (
|
||||
PrefixHypothesis = "H-"
|
||||
PrefixRevision = "R-"
|
||||
PrefixExperiment = "E-"
|
||||
PrefixPressure = "P-"
|
||||
PrefixBackendRequirement = "BR-"
|
||||
PrefixDecision = "D-"
|
||||
PrefixEvent = "EV-"
|
||||
PrefixFeedback = "F-"
|
||||
PrefixCohort = "C-"
|
||||
)
|
||||
|
||||
// EntityKind names the FLUID artifact an identifier refers to.
|
||||
type EntityKind string
|
||||
|
||||
const (
|
||||
KindHypothesis EntityKind = "hypothesis"
|
||||
KindRevision EntityKind = "revision"
|
||||
KindExperiment EntityKind = "experiment"
|
||||
KindPressure EntityKind = "pressure"
|
||||
KindBackendRequirement EntityKind = "backend_requirement"
|
||||
KindDecision EntityKind = "decision"
|
||||
KindEvent EntityKind = "event"
|
||||
KindFeedback EntityKind = "feedback"
|
||||
KindCohort EntityKind = "cohort"
|
||||
)
|
||||
|
||||
// prefixOrder matters: "BR-" must be tested before "B"-less single letters
|
||||
// would otherwise mis-claim it, and "EV-" before "E-".
|
||||
var prefixOrder = []struct {
|
||||
prefix string
|
||||
kind EntityKind
|
||||
}{
|
||||
{PrefixBackendRequirement, KindBackendRequirement},
|
||||
{PrefixEvent, KindEvent},
|
||||
{PrefixHypothesis, KindHypothesis},
|
||||
{PrefixRevision, KindRevision},
|
||||
{PrefixExperiment, KindExperiment},
|
||||
{PrefixPressure, KindPressure},
|
||||
{PrefixDecision, KindDecision},
|
||||
{PrefixFeedback, KindFeedback},
|
||||
{PrefixCohort, KindCohort},
|
||||
}
|
||||
|
||||
// KindOf reports which FLUID artifact an identifier names.
|
||||
//
|
||||
// Audit trails carry bare identifiers across record boundaries, so being able to
|
||||
// classify one without knowing where it came from keeps trace reconstruction
|
||||
// from needing a lookup table at every hop.
|
||||
func KindOf(id string) (EntityKind, bool) {
|
||||
for _, p := range prefixOrder {
|
||||
if strings.HasPrefix(id, p.prefix) {
|
||||
return p.kind, true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// ErrWrongKind reports an identifier used in the wrong position.
|
||||
type ErrWrongKind struct {
|
||||
ID string
|
||||
Want EntityKind
|
||||
Got EntityKind
|
||||
}
|
||||
|
||||
func (e *ErrWrongKind) Error() string {
|
||||
if e.Got == "" {
|
||||
return fmt.Sprintf("identifier %q has no recognized FLUID prefix, wanted a %s id", e.ID, e.Want)
|
||||
}
|
||||
return fmt.Sprintf("identifier %q is a %s id, wanted a %s id", e.ID, e.Got, e.Want)
|
||||
}
|
||||
|
||||
// RequireKind checks that id names the expected artifact.
|
||||
func RequireKind(id string, want EntityKind) error {
|
||||
got, ok := KindOf(id)
|
||||
if !ok || got != want {
|
||||
return &ErrWrongKind{ID: id, Want: want, Got: got}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue