package taskloop import ( "crypto/sha256" _ "embed" "encoding/hex" "fmt" "os" "path/filepath" "sort" "strings" "gopkg.in/yaml.v3" ) const paritySchemaVersion = 1 //go:embed testdata/parity.yaml var embeddedParityManifest []byte // ParityManifest records the S13 disposition of each retained reference // behavior. It is intentionally evidence-only: runtime behavior remains in // the shared agenttask and agentpolicy packages. type ParityManifest struct { SchemaVersion int `yaml:"schema_version"` ReferenceRoot string `yaml:"reference_root"` DeletionGate string `yaml:"deletion_gate"` Behaviors []ParityBehavior `yaml:"behaviors"` Disposal ParityDisposal `yaml:"disposal"` } type ParityBehavior struct { ID string `yaml:"id"` ReferenceSources []string `yaml:"reference_sources"` Disposition string `yaml:"disposition"` GoOwner string `yaml:"go_owner"` GoSource []string `yaml:"go_source"` GoTest []string `yaml:"go_test"` Invariants string `yaml:"invariants"` ProductionCaller string `yaml:"production_caller"` } type ParityDisposal struct { State string `yaml:"state"` Procedure string `yaml:"procedure"` ReferenceFiles []ParityReferenceFile `yaml:"reference_files"` } type ParityReferenceFile struct { Path string `yaml:"path"` SHA256 string `yaml:"sha256"` Disposition string `yaml:"disposition"` CoveredBy []string `yaml:"covered_by"` } // LoadEmbeddedParityManifest returns the repository-owned S13 manifest. func LoadEmbeddedParityManifest() (ParityManifest, error) { return ParseParityManifest(embeddedParityManifest) } // ValidateEmbeddedParityManifest validates the checked-in manifest against the // supplied repository root and returns its stable operator report. func ValidateEmbeddedParityManifest(root string) (string, error) { manifest, err := LoadEmbeddedParityManifest() if err != nil { return "", err } if err := ValidateParityManifest(root, manifest); err != nil { return "", err } return FormatParityReport(manifest), nil } // RepositoryRoot walks upward from start until it finds the module root. func RepositoryRoot(start string) (string, error) { directory, err := filepath.Abs(start) if err != nil { return "", err } for { if info, err := os.Stat(filepath.Join(directory, "go.mod")); err == nil && !info.IsDir() { return directory, nil } parent := filepath.Dir(directory) if parent == directory { return "", fmt.Errorf("repository root not found from %q", start) } directory = parent } } func ParseParityManifest(data []byte) (ParityManifest, error) { var manifest ParityManifest if err := yaml.Unmarshal(data, &manifest); err != nil { return ParityManifest{}, fmt.Errorf("parity manifest: decode: %w", err) } return manifest, nil } // ValidateParityManifest proves that the inventory is complete in retained // mode, or completely absent in disposed mode. The latter keeps the recorded // checksums as historical pre-deletion evidence without requiring deleted // files to exist. func ValidateParityManifest(root string, manifest ParityManifest) error { if manifest.SchemaVersion != paritySchemaVersion { return fmt.Errorf("parity manifest: unsupported schema_version %d", manifest.SchemaVersion) } if strings.TrimSpace(manifest.ReferenceRoot) == "" || strings.TrimSpace(manifest.DeletionGate) == "" { return fmt.Errorf("parity manifest: reference_root and deletion_gate are required") } if len(manifest.Behaviors) == 0 { return fmt.Errorf("parity manifest: behaviors are required") } behaviorIDs := make(map[string]struct{}, len(manifest.Behaviors)) behaviorSources := make(map[string]struct{}) for _, behavior := range manifest.Behaviors { if strings.TrimSpace(behavior.ID) == "" { return fmt.Errorf("parity manifest: unclassified behavior") } if _, exists := behaviorIDs[behavior.ID]; exists { return fmt.Errorf("parity manifest: duplicate behavior %q", behavior.ID) } behaviorIDs[behavior.ID] = struct{}{} if !allowedDisposition(behavior.Disposition) { return fmt.Errorf("parity manifest: behavior %q has unclassified disposition %q", behavior.ID, behavior.Disposition) } if strings.TrimSpace(behavior.GoOwner) == "" || strings.TrimSpace(behavior.Invariants) == "" || behavior.ProductionCaller != "go-only" { return fmt.Errorf("parity manifest: behavior %q lacks Go ownership evidence", behavior.ID) } if len(behavior.GoSource) == 0 || len(behavior.GoTest) == 0 { return fmt.Errorf("parity manifest: behavior %q lacks concrete Go evidence", behavior.ID) } for _, source := range behavior.GoSource { if err := validateLiteralExistingPath(root, source); err != nil { return fmt.Errorf("parity manifest: behavior %q: %w", behavior.ID, err) } } for _, source := range behavior.ReferenceSources { if err := validateLiteralPath(source); err != nil { return fmt.Errorf("parity manifest: behavior %q: %w", behavior.ID, err) } behaviorSources[source] = struct{}{} } for _, testName := range behavior.GoTest { if strings.TrimSpace(testName) == "" { return fmt.Errorf("parity manifest: behavior %q has empty Go test", behavior.ID) } } } if strings.TrimSpace(manifest.Disposal.Procedure) == "" { return fmt.Errorf("parity manifest: disposal procedure is required") } if !allowedDisposalState(manifest.Disposal.State) { return fmt.Errorf("parity manifest: unknown disposal state %q", manifest.Disposal.State) } discovered, err := discoverParityReferenceFiles(root, manifest.ReferenceRoot) if err != nil { return err } discoveredSet := make(map[string]struct{}, len(discovered)) for _, path := range discovered { discoveredSet[path] = struct{}{} } disposal := make(map[string]ParityReferenceFile, len(manifest.Disposal.ReferenceFiles)) for _, reference := range manifest.Disposal.ReferenceFiles { if _, exists := disposal[reference.Path]; exists { return fmt.Errorf("parity manifest: duplicate disposal path %q", reference.Path) } if !allowedDisposition(reference.Disposition) || len(reference.CoveredBy) == 0 { return fmt.Errorf("parity manifest: disposal entry %q is unclassified", reference.Path) } if err := validateLiteralPath(reference.Path); err != nil { return fmt.Errorf("parity manifest: disposal entry: %w", err) } for _, behaviorID := range reference.CoveredBy { if _, exists := behaviorIDs[behaviorID]; !exists { return fmt.Errorf("parity manifest: disposal entry %q covers unknown behavior %q", reference.Path, behaviorID) } } if len(reference.SHA256) != sha256.Size*2 { return fmt.Errorf("parity manifest: invalid checksum for %q", reference.Path) } disposal[reference.Path] = reference } if err := validateParityInventory(behaviorSources, disposal); err != nil { return err } if manifest.Disposal.State == "disposed" { if len(discovered) != 0 { return fmt.Errorf("parity manifest: disposed state has retained fixture %q", discovered[0]) } for path := range disposal { if _, err := os.Lstat(filepath.Join(root, filepath.FromSlash(path))); err == nil { return fmt.Errorf("parity manifest: disposed state has survivor %q", path) } else if !os.IsNotExist(err) { return fmt.Errorf("parity manifest: inspect disposed entry %q: %w", path, err) } } return nil } for _, path := range discovered { if _, exists := disposal[path]; !exists { return fmt.Errorf("parity manifest: unrecorded retained fixture %q", path) } if _, exists := behaviorSources[path]; !exists { return fmt.Errorf("parity manifest: missing behavior classification for %q", path) } } for path, reference := range disposal { if _, exists := discoveredSet[path]; !exists { return fmt.Errorf("parity manifest: retained state is missing fixture %q", path) } contents, err := os.ReadFile(filepath.Join(root, filepath.FromSlash(path))) if err != nil { return fmt.Errorf("parity manifest: read disposal entry %q: %w", path, err) } digest := sha256.Sum256(contents) if !strings.EqualFold(reference.SHA256, hex.EncodeToString(digest[:])) { return fmt.Errorf("parity manifest: checksum drift for %q", path) } } return nil } // validateParityInventory keeps disposition evidence anchored to the complete // classified source inventory even after the files themselves are disposed. func validateParityInventory(behaviorSources map[string]struct{}, disposal map[string]ParityReferenceFile) error { missing := make([]string, 0) for path := range behaviorSources { if _, exists := disposal[path]; !exists { missing = append(missing, path) } } if len(missing) != 0 { sort.Strings(missing) return fmt.Errorf("parity manifest: disposal inventory is missing classified reference %q", missing[0]) } extra := make([]string, 0) for path := range disposal { if _, exists := behaviorSources[path]; !exists { extra = append(extra, path) } } if len(extra) != 0 { sort.Strings(extra) return fmt.Errorf("parity manifest: disposal inventory has unclassified reference %q", extra[0]) } return nil } // discoverParityReferenceFiles returns every retained Python source or test // fixture below the manifest-owned root in deterministic repository-relative // order. The manifest must enumerate this result exactly. func discoverParityReferenceFiles(root, referenceRoot string) ([]string, error) { if strings.TrimSpace(referenceRoot) == "" || filepath.IsAbs(referenceRoot) { return nil, fmt.Errorf("parity manifest: invalid reference_root %q", referenceRoot) } cleaned := filepath.Clean(filepath.FromSlash(referenceRoot)) if cleaned == "." || cleaned == ".." || strings.HasPrefix(cleaned, ".."+string(filepath.Separator)) { return nil, fmt.Errorf("parity manifest: invalid reference_root %q", referenceRoot) } directory := filepath.Join(root, cleaned) info, err := os.Stat(directory) if err != nil { return nil, fmt.Errorf("parity manifest: reference_root %q: %w", referenceRoot, err) } if !info.IsDir() { return nil, fmt.Errorf("parity manifest: reference_root %q must be a directory", referenceRoot) } var files []string err = filepath.WalkDir(directory, func(path string, entry os.DirEntry, walkErr error) error { if walkErr != nil { return walkErr } if entry.IsDir() || !entry.Type().IsRegular() { return nil } name := entry.Name() if !strings.HasSuffix(name, ".py") && !strings.HasSuffix(name, ".py.orig") { return nil } relative, err := filepath.Rel(root, path) if err != nil { return err } files = append(files, filepath.ToSlash(relative)) return nil }) if err != nil { return nil, fmt.Errorf("parity manifest: discover retained fixtures: %w", err) } sort.Strings(files) return files, nil } // FormatParityReport emits a bounded, stable report for the operator command. func FormatParityReport(manifest ParityManifest) string { ids := make([]string, 0, len(manifest.Behaviors)) for _, behavior := range manifest.Behaviors { ids = append(ids, behavior.ID) } sort.Strings(ids) return fmt.Sprintf("parity: ok\n schema_version: %d\n behaviors: %d\n disposal_state: %s\n disposal_files: %d\n deletion_gate: %s\n ids: %s\n", manifest.SchemaVersion, len(ids), manifest.Disposal.State, len(manifest.Disposal.ReferenceFiles), manifest.DeletionGate, strings.Join(ids, ",")) } func allowedDisposition(value string) bool { return value == "absorb" || value == "replace" || value == "not-applicable" } func allowedDisposalState(value string) bool { return value == "retained" || value == "disposed" } func validateLiteralPath(value string) error { if strings.TrimSpace(value) == "" || strings.ContainsAny(value, "*?[") || filepath.IsAbs(value) { return fmt.Errorf("invalid literal path %q", value) } cleaned := filepath.Clean(filepath.FromSlash(value)) if cleaned == "." || cleaned == ".." || strings.HasPrefix(cleaned, ".."+string(filepath.Separator)) { return fmt.Errorf("invalid literal path %q", value) } return nil } func validateLiteralExistingPath(root, value string) error { if err := validateLiteralPath(value); err != nil { return err } cleaned := filepath.Clean(filepath.FromSlash(value)) info, err := os.Stat(filepath.Join(root, cleaned)) if err != nil { return fmt.Errorf("stale path %q: %w", value, err) } if info.IsDir() { return fmt.Errorf("path %q must be a file", value) } return nil }