State Model - AFPS Tracker
Confirmed logical domain model for the approved AFPS Tracker first repo read-through. The canonical domain model and model-tree manifest have been written, the flow-tree branches are attached to the model, approved glossary terms were appended, and the working cursor was archived.
Decision Summary
Approval result: the logical model is approved as the AFPS Tracker substrate for UX variation work. The model is coherent across all five framework passes and keeps AFPS Tracker read-first, source-native, and subordinate to canonical AFPS artifacts.
ProductPath owns selected-path interpretation, source-backed claims, evidence, provenance, diagnostics, and handoff eligibility.PortfolioSnapshot and path refs support orientation and unresolved/excluded path visibility without making repo scan the central aggregate.TrustEnvelope preserves trust level, parser confidence, source confidence, warnings, and diagnostic refs separately.Framework Evidence
| Framework | Intermediate | Decision Contribution |
|---|---|---|
| Event modeling | docs/history/archive/2026-07-02/002008/design/afps-tracker/state-model-afps-tracker/event-modeling.md | Timeline of scan, manifest interpretation, selection, source resolution, diagnostics, evidence/provenance inspection, handoff generation, copy, and boundary explanation. |
| Domain model | docs/history/archive/2026-07-02/002008/design/afps-tracker/state-model-afps-tracker/domain-model.md | DDD spine with ProductPath as the main aggregate root and Portfolio as a thin orientation aggregate. |
| State machine | docs/history/archive/2026-07-02/002008/design/afps-tracker/state-model-afps-tracker/state-machine.md | Lifecycles for portfolio, product path, source artifact, evidence link, provenance record, diagnostic issue, and handoff summary. |
| Data model | docs/history/archive/2026-07-02/002008/design/afps-tracker/state-model-afps-tracker/data-model.md | Logical ERD, attributes, relationships, read models, and integrity rules, including explicit unresolved refs. |
| Contract map | docs/history/archive/2026-07-02/002008/design/afps-tracker/state-model-afps-tracker/contract-map.md | Logical command/query contracts and response shapes with trust envelopes, diagnostic refs, and boundary suppression. |
Confirmed Domain Model
The following content was written to design/afps-tracker/domain-model-afps-tracker.md after compiled YAML approval.
---
skill: state-model
status: confirmed
topic: afps-tracker
product_path: research/afps-tracker
source_command: "$state-model afps-tracker"
flow_tree: design/afps-tracker/flow-tree-afps-tracker.yaml
model_tree: design/afps-tracker/model-tree-afps-tracker.yaml
alignment_page: alignment/state-model-afps-tracker.html
---
# AFPS Tracker Domain Model
## Scope
This logical model defines the entities, value objects, state machines, events, commands, read models, policies, and contracts for the approved AFPS Tracker first repo read-through. The model is anchored to the approved flow-tree nodes:
- `uf-orient-portfolio`
- `uf-verify-selected-path`
- `uf-inspect-source-provenance`
- `uf-recover-diagnostics`
- `uf-handoff-resume`
- `uf-explain-boundaries`
AFPS Tracker remains read-first and source-native. Canonical truth stays in AFPS workflow artifacts: `research/.progress.yaml`, scoped research documents, alignment pages, canonical design artifacts, archive snapshots, and working packets where relevant. This model does not define storage, real endpoints, URLs, auth, migrations, indexes, deployment, file watchers, or runtime mechanics.
## Ubiquitous-Language Glossary
| Term | Meaning |
| --- | --- |
| Repository context | Logical context for one repo-local AFPS read-through. |
| Portfolio snapshot | Source-derived grouping of active, parallel, inactive, excluded, and unresolved product paths for a scan. |
| Portfolio path ref | A source active/excluded/unresolved path reference, whether or not it resolves to a ProductPath. |
| Product path | A source-derived AFPS research/product path interpreted from canonical AFPS artifacts. |
| Source artifact | A canonical AFPS file or generated artifact used as evidence, provenance, diagnostics, or handoff source. |
| Source-backed claim | A displayed or handoffable assertion whose support must be traceable or explicitly caveated. |
| Evidence link | Logical link from a claim to a source artifact, field, section, approval record, or source hint. |
| Provenance record | Approval, canonicalization, amendment, archive, review, or working-packet lineage. |
| Diagnostic issue | Trust degradation or recovery item affecting source, claim, path, portfolio, or handoff level. |
| Trust envelope | Response wrapper carrying trust level, parser confidence, source confidence, warnings, and diagnostic refs. |
| Parser confidence | Confidence in extraction or mapping from a source artifact. |
| Source confidence | Confidence that the underlying source/provenance supports the claim. |
| Handoff eligibility | Derived availability of clean, warning, blocked, explanatory, promoted-boundary, or out-of-scope output. |
| Boundary kind | Why a path or request cannot produce normal active AFPS routing. |
| Next-command suppression | Explicit suppression of normal next-command output under blocked, promoted, inactive, contradicted, unsupported, or out-of-scope states. |
## Entities And Value Objects
| Element | Kind | Key attributes | Relationships | Flow bindings |
| --- | --- | --- | --- | --- |
| RepositoryContext | entity | repository_ref, scan_started_at, scan_completed_at, scan_state, source_health, warnings | Owns zero-or-one PortfolioSnapshot and discovered source artifacts/diagnostics for the read-through. | `uf-orient-portfolio` triggers/displays; `uf-recover-diagnostics` displays |
| PortfolioSnapshot | aggregate-root | snapshot_id, repository_ref, progress_manifest_ref, snapshot_state, active_path_refs, excluded_path_refs, parser_confidence, source_confidence | Has many PortfolioPathRefs and ProductPaths. | `uf-orient-portfolio` reads/displays; `uf-explain-boundaries` reads; `uf-recover-diagnostics` transitions |
| PortfolioPathRef | entity | path_ref_id, path_id, scope_path, source_status, resolution_state, boundary_kind, diagnostic_refs | Resolves to zero-or-one ProductPath and may raise diagnostics. | `uf-orient-portfolio` displays; `uf-explain-boundaries` displays; `uf-recover-diagnostics` writes |
| ProductPath | aggregate-root | path_id, label, scope_path, source_status, pipeline_stage, reason, next_skill, path_state, trust_level, handoff_eligibility, boundary_kind | Owns source artifacts, claims, provenance, diagnostics, and handoff summaries. | `uf-orient-portfolio`, `uf-verify-selected-path`, `uf-handoff-resume`, `uf-explain-boundaries` |
| SourceArtifact | entity | artifact_id, artifact_ref, artifact_kind, source_role, read_status, approval_status, freshness_state, parser_confidence, source_confidence, warnings | Supports claims and provenance; can raise diagnostics. | `uf-verify-selected-path`, `uf-inspect-source-provenance`, `uf-recover-diagnostics` |
| SourceBackedClaim | entity | claim_id, path_id, claim_kind, claim_text, support_status, parser_confidence, source_confidence, trust_level, derived_from, warnings | Has zero-or-many EvidenceLinks and Diagnostics. | `uf-verify-selected-path`, `uf-inspect-source-provenance`, `uf-recover-diagnostics`, `uf-handoff-resume` |
| EvidenceLink | entity | evidence_link_id, claim_id, evidence_ref, resolved_ref, resolution_state, support_status, read_status, source_hint, warnings | Links a claim to zero-or-one resolved SourceArtifact. | `uf-inspect-source-provenance`, `uf-recover-diagnostics` |
| ProvenanceRecord | entity | provenance_id, path_id, provenance_ref, provenance_kind, approval_state, decision_summary, canonical_target, archive_refs, confidence, warnings | Supports claims, artifacts, and handoff confidence. | `uf-inspect-source-provenance`, `uf-recover-diagnostics`, `uf-handoff-resume` |
| DiagnosticIssue | entity | issue_id, issue_kind, severity, level, state, affected_refs, affected_claims, affected_paths, handoff_impact, recommended_manual_check | Attaches at the narrowest source, claim, path, portfolio, or handoff level. | `uf-recover-diagnostics`, `uf-orient-portfolio`, `uf-handoff-resume` |
| HandoffSummary | entity/read-model output | summary_id, path_id, handoff_type, copy_state, confidence, next_skill, branch_state_explanation, handoff_boundary, included refs, blocked_reasons | Reads path, claims, evidence, provenance, and diagnostics; may be copied only when policies permit. | `uf-handoff-resume`, `uf-explain-boundaries` |
| ArtifactRef | value-object | path, section_hint, artifact_kind, source_role | Used by source, evidence, provenance, and diagnostics. | `uf-verify-selected-path`, `uf-inspect-source-provenance` |
| TrustLevel | value-object | clean, warning, blocked, partial, inferred, unsupported, contradicted, source-backed, explanatory, out-of-scope | Used by paths, claims, diagnostics, and handoffs. | `uf-recover-diagnostics`, `uf-handoff-resume` |
| ParserConfidence | value-object | high, medium, low, unknown, rationale | Kept separate from source confidence. | `uf-orient-portfolio`, `uf-verify-selected-path`, `uf-recover-diagnostics` |
| SourceConfidence | value-object | confirmed, partial, review-only, unsupported, contradicted, missing, unknown | Kept separate from parser confidence. | `uf-inspect-source-provenance`, `uf-recover-diagnostics`, `uf-handoff-resume` |
| HandoffEligibility | value-object | clean-copy, warning-copy, blocked, explanatory, promoted-boundary, out-of-scope, unavailable | Derived by policy from path state, trust, diagnostics, and boundary kind. | `uf-handoff-resume`, `uf-explain-boundaries` |
| BoundaryKind | value-object | inactive, archived, deferred, revisit-candidate, promoted, out-of-scope, unsupported, unresolved | Suppresses normal active routing. | `uf-explain-boundaries`, `uf-handoff-resume` |
## State Machines
### PortfolioSnapshot
States: `unscanned`, `scanning`, `no-afps-state`, `manifest-malformed`, `paths-discovered`, `partial`, `source-backed`, `blocked`.
Key transitions: `ScanPortfolio` moves `unscanned -> scanning`; missing source moves to `no-afps-state`; malformed manifest moves to `manifest-malformed`; discovered paths move to `paths-discovered`; unresolved refs or non-blocking diagnostics move to `partial`; classified clean source state moves to `source-backed`; blocking portfolio diagnostics move to `blocked`.
UI mappings: empty/no-manifest overview, loading portfolio overview, grouped path rows, partial/warning badges, blocking portfolio diagnostic, clean portfolio overview.
### ProductPath
States: `undiscovered`, `discovered`, `selected`, `source-inventorying`, `source-backed`, `partial`, `warning`, `blocked`, `inactive`, `promoted-boundary`, `out-of-scope`.
Key transitions: discovered source record creates `discovered`; excluded or inactive source status moves to `inactive`; promoted state moves to `promoted-boundary`; selection moves to `selected`; source inventory moves to `source-inventorying`; supported claims move to `source-backed`; unresolved non-core support moves to `partial`; warning diagnostics move to `warning`; contradictions, unreadable core source, malformed core source, or next-skill inconsistency move to `blocked`; out-of-scope requests move to `out-of-scope`.
UI mappings: portfolio row, selected-path loading frame, clean detail, partial detail, warning caveats, blocked detail, inactive explanation, promoted boundary explanation, out-of-scope explanation.
### SourceArtifact
States: `referenced`, `resolving`, `found`, `parsed`, `confirmed`, `review-only`, `amended`, `archived`, `missing`, `unreadable`, `malformed`, `stale`, `unsupported`.
Key transitions: source inventory or evidence resolution moves to `resolving`; readable artifacts move to `found`; parsed facts move to `parsed`; confirmed provenance moves to `confirmed`; review-state provenance moves to `review-only`; amendment or archive lineage moves to `amended` or `archived`; missing, permission, malformed, stale, or unsupported support moves to the corresponding diagnostic state.
### EvidenceLink
States: `unresolved`, `resolving`, `resolved`, `supports-claim`, `partially-supports-claim`, `contradicts-claim`, `inaccessible`.
Key transitions: inspection moves unresolved links to resolving; resolved source refs move to `resolved`; claim evaluation moves to supported, partial, or contradictory support; inaccessible source preview moves to `inaccessible`; source review can return partial or contradictory links to resolving.
### ProvenanceRecord
States: `missing`, `discovered`, `review`, `confirmed`, `amended`, `archived`, `partial`, `contradicted`.
Key transitions: discovered records move to `discovered`; confirmed approval/canonical target moves to `confirmed`; unapproved review state moves to `review`; missing targets move to `missing`; amendments and archives preserve lineage; partial or contradictory provenance degrades trust and can return to discovered after source review.
### DiagnosticIssue
Severity states: `none`, `raised`, `classified`, `warning`, `blocked`, `partial`, `resolved-by-source-review`.
Affected-level dimension: `source-level`, `claim-level`, `path-level`, `portfolio-level`, `handoff-level`.
Rule: severity and affected level are separate dimensions. Diagnostics degrade trust at the narrowest affected scope first, then path and handoff levels only when needed.
### HandoffSummary
States: `unavailable`, `requested`, `clean-copy`, `warning-copy`, `blocked`, `explanatory`, `promoted-boundary`, `out-of-scope`, `copied`, `suppressed`.
Key transitions: `GenerateHandoff` moves unavailable summaries to requested; clean source-backed selected paths move to clean-copy; non-blocking warnings move to warning-copy; blocking diagnostics move to blocked; inactive, promoted, or out-of-scope states move to explanatory/boundary states; permitted copy moves to copied; blocked copy moves to suppressed.
## Events
Core events: `RepositoryScanStarted`, `ProgressManifestLocated`, `ProgressManifestMissing`, `ProgressManifestParsed`, `ProgressManifestMalformed`, `ActivePathsNormalized`, `ProductPathRecordDiscovered`, `ProductPathRecordExcluded`, `ActivePathReferenceUnresolved`, `ProductPathSelected`, `ScopedSourceInventoryStarted`, `ScopedResearchArtifactResolved`, `ScopedResearchArtifactMissing`, `EvidenceRefResolutionStarted`, `EvidenceRefResolved`, `EvidenceRefUnresolved`, `ClaimSupportEvaluated`, `ProvenanceRecordDiscovered`, `ProvenanceRecordConfirmed`, `ProvenanceRecordReviewOnly`, `ProvenanceRecordArchived`, `ProvenanceRecordMissing`, `DiagnosticIssueRaised`, `DiagnosticIssueClassified`, `TrustLevelChanged`, `HandoffEligibilityChanged`, `ClaimInspectionRequested`, `SourcePreviewPrepared`, `SourcePreviewUnavailable`, `NextSkillCandidateDetected`, `NextSkillCandidateSuppressed`, `HandoffGenerationRequested`, `HandoffSummaryPrepared`, `CleanCopyPrepared`, `WarningCopyPrepared`, `HandoffBlocked`, `HandoffCopyRequested`, `HandoffCopied`, `HandoffCopySuppressed`, `BoundaryExplanationRequested`, `PromotedBoundaryDetected`, `InactivePathExplained`, `OutOfScopeRequestExplained`.
## Commands And Queries
Commands: `ScanPortfolio`, `SelectProductPath`, `EvaluateDiagnostics`, `GenerateHandoff`, `CopyHandoff`.
Queries: `ListPortfolioPaths`, `GetSelectedPathDetail`, `InspectEvidenceRef`, `InspectProvenance`, `GetDiagnostics`, `ExplainBoundary`.
Shared logical types: `RepositoryContextInput`, `ProductPathSelector`, `ArtifactRef`, `ClaimRef`, `TrustEnvelope`, `DiagnosticRef`, `HandoffBoundary`.
Every trust-bearing response must carry `trust_level`, `parser_confidence`, `source_confidence`, `warnings[]`, and `diagnostic_refs[]`. Every claim-displaying response must carry `claim_id`, support status, evidence/provenance refs, and caveats. Boundary and handoff contracts must expose `normal_route_suppressed` and suppression reason when a next command is not safe.
## Read Models
| Read model | Source elements | Purpose |
| --- | --- | --- |
| Portfolio Overview | RepositoryContext, PortfolioSnapshot, PortfolioPathRef, ProductPath, DiagnosticIssue, SourceArtifact | Show active, parallel, inactive, excluded, unresolved, warning, and blocked portfolio state. |
| Selected Path Detail | ProductPath, SourceBackedClaim, SourceArtifact, EvidenceLink, ProvenanceRecord, DiagnosticIssue | Show selected path status, stage, reason, next skill, refs, trust, warnings, and handoff eligibility. |
| Evidence Inspector | SourceBackedClaim, EvidenceLink, SourceArtifact, DiagnosticIssue | Verify displayed claims against source support or visible unresolved/contradictory state. |
| Provenance Timeline | ProvenanceRecord, SourceArtifact, SourceBackedClaim, DiagnosticIssue | Show approval, canonicalization, amendment, archive, review, missing, partial, or contradictory provenance. |
| Diagnostics Panel | DiagnosticIssue plus affected source/claim/path/handoff refs | Show severity, affected level, handoff impact, recommended checks, and trust changes. |
| Handoff Summary | HandoffSummary, ProductPath, SourceBackedClaim, EvidenceLink, ProvenanceRecord, DiagnosticIssue | Produce clean, warning, blocked, explanatory, promoted-boundary, or out-of-scope output with caveats preserved. |
## Policies
| Policy | Rule |
| --- | --- |
| Source-backed display rule | A displayed claim can be labeled source-backed only when evidence or provenance supports it. |
| Unresolved ref preservation rule | Unresolved active refs, evidence refs, provenance refs, and source refs stay modeled with diagnostics. |
| Boundary suppression rule | Inactive, promoted, archived, deferred, revisit-candidate, unsupported, unresolved, contradicted, or out-of-scope states cannot emit normal clean next-skill handoffs. |
| Confidence separation rule | Parser confidence and source confidence are distinct and may degrade independently. |
| Narrowest diagnostic scope rule | Diagnostics attach to source, claim, path, portfolio, or handoff level at the narrowest affected level first. |
| Warning preservation rule | Warning and partial handoffs are allowed only when caveats are included in output. |
| Review-only provenance rule | Review-state pages or unconfirmed artifacts cannot support confirmed provenance claims. |
| Next-command suppression rule | Missing, contradicted, blocked, out-of-scope, or promoted-boundary next-skill values suppress normal next-command output. |
## Logical Contracts
### ScanPortfolio
Request: repository context, optional inactive/diagnostics flags, optional source refs.
Response: scan state, repository ref, portfolio snapshot ID, source health trust envelope, path counts, active/excluded/unresolved refs, diagnostics, updated read models.
Errors: no AFPS state, missing/malformed progress manifest, permission denied, partial scan, unreadable source.
### SelectProductPath
Request: repository ref, product path selector, optional portfolio snapshot ID, selection reason, detail depth.
Response: selected path ID, selection state, path state, scope path, trust envelope, source inventory status, available/blocked actions, diagnostics.
Errors: path not found, path excluded, active ref unresolved, scope path missing, selection blocked, source state contradicted.
### EvaluateDiagnostics
Request: repository ref, optional path, claim refs, artifact refs, evaluation reason, prior diagnostics.
Response: diagnostics, trust changes, handoff eligibility, affected read models, manual checks, warnings to preserve, blocked reasons.
Errors: diagnostic source missing, diagnostic scope ambiguous, cannot classify issue, source review required.
### GenerateHandoff
Request: product path selector, handoff intent, detail level, include refs/diagnostics.
Response: summary ID, handoff type/state, path state, optional next skill, branch-state explanation, trust envelope, evidence/provenance refs, warnings, blocked reasons, boundary, copy allowed, normal next command suppression flag.
Errors: handoff unavailable, blocking diagnostics present, next skill unsupported, boundary explanation required, source support insufficient, out-of-scope request.
### CopyHandoff
Request: summary ID, copy intent, acknowledged warnings, requested format.
Response: copied/suppressed/blocked state, warnings preserved, suppression reason, copied payload shape, diagnostic refs, boundary.
Errors: summary not found, warnings not preserved, copy blocked by diagnostics, next command suppressed, unsupported copy format.
### ListPortfolioPaths
Request: repository ref, optional snapshot, filters, selected path.
Response: path groups, active/parallel/inactive/excluded/unresolved refs, source health, diagnostics, available actions.
### GetSelectedPathDetail
Request: selector plus claims/refs/diagnostics flags.
Response: path fields, trust envelope, claims, evidence/provenance refs, diagnostics, handoff eligibility, available/blocked actions.
### InspectEvidenceRef
Request: claim ref, optional evidence ref, path ID, source hint, preview flag.
Response: claim, evidence link, resolved ref, resolution/read/support state, trust envelope, unsupported claims, diagnostics, preview availability.
### InspectProvenance
Request: optional path, claim ref, artifact ref, include archives/review flags.
Response: records, approval state, confirmed/review-only records, archive refs, canonical targets, source confidence, warnings, diagnostics, handoff relevance.
### GetDiagnostics
Request: optional repository, path, claim refs, levels, severities, include resolved.
Response: issues, summaries by level/severity, affected sources/claims/paths, handoff impacts, manual checks, trust changes.
### ExplainBoundary
Request: selector, optional boundary kind, requested-by, include refs.
Response: path, boundary kind, source status, branch-state explanation, route suppression, suppression reason, promoted boundary flag, evidence/provenance refs, warnings, diagnostics, next-step visibility.
## Evidence And Flow Binding Matrix
All model elements bind to approved flow nodes through the model-tree manifest only. Bindings are not duplicated into the flow tree. See `design/afps-tracker/model-tree-afps-tracker.yaml` for element-level `flow_bindings`.
## Assumptions And Confidence
Confidence is high that ProductPath is the correct selected-path aggregate root because all five framework passes converged on that boundary. Confidence is medium that PortfolioSnapshot should be represented as a thin aggregate root because it is necessary for grouped orientation and unresolved refs but should not own selected-path trust. Confidence is high that parser confidence and source confidence must stay separate.
Remaining risk: the approved flow tree still has `evaluation_priority: pending-key-moments`; this model covers the full approved first read-through and can be narrowed later through normal modify-back if key-moments promotes only a subset.
## Physical Concern Deferrals
Deferred to `$spec-interview`: persistence, caches, storage engines, indexes, migrations, file watchers, parser implementation, API routes, endpoint URLs, transports, auth, authorization, sandbox mechanics, deployment, source preview mechanics, clipboard implementation, and high-fidelity UI layout.
Confirmed Model-Tree Manifest
The following content was written to design/afps-tracker/model-tree-afps-tracker.yaml after compiled YAML approval.
schema_version: v0.1
mode: product-path
topic: afps-tracker
product_path: research/afps-tracker
status: confirmed
flow_tree_ref: design/afps-tracker/flow-tree-afps-tracker.yaml
source_artifacts:
- docs/history/archive/2026-07-02/002008/design/afps-tracker/_working/state-model-afps-tracker-brief.md
- docs/history/archive/2026-07-02/002008/docs/history/archive/2026-07-02/002008/design/afps-tracker/state-model-afps-tracker/event-modeling.md
- docs/history/archive/2026-07-02/002008/docs/history/archive/2026-07-02/002008/design/afps-tracker/state-model-afps-tracker/domain-model.md
- docs/history/archive/2026-07-02/002008/docs/history/archive/2026-07-02/002008/design/afps-tracker/state-model-afps-tracker/state-machine.md
- docs/history/archive/2026-07-02/002008/docs/history/archive/2026-07-02/002008/design/afps-tracker/state-model-afps-tracker/data-model.md
- docs/history/archive/2026-07-02/002008/docs/history/archive/2026-07-02/002008/design/afps-tracker/state-model-afps-tracker/contract-map.md
- design/afps-tracker/flow-tree-afps-tracker.yaml
- design/afps-tracker/user-flow-afps-tracker.md
frameworks:
- event-modeling
- domain-model
- state-machine
- data-model
- contract-map
entities:
- id: repository-context
name: RepositoryContext
kind: entity
attributes: [repository_ref, scan_started_at, scan_completed_at, scan_state, source_health, warnings]
relationships:
- target: portfolio-snapshot
cardinality: one-to-zero-or-one
- target: source-artifact
cardinality: one-to-many
- target: diagnostic-issue
cardinality: one-to-many
flow_bindings:
- { flow_node_ref: uf-orient-portfolio, access: triggers }
- { flow_node_ref: uf-orient-portfolio, access: displays }
- { flow_node_ref: uf-recover-diagnostics, access: displays }
- id: portfolio-snapshot
name: PortfolioSnapshot
kind: aggregate-root
attributes: [snapshot_id, repository_ref, progress_manifest_ref, snapshot_state, active_path_refs, excluded_path_refs, parser_confidence, source_confidence]
lifecycle_ref: portfolio-snapshot-lifecycle
relationships:
- target: portfolio-path-ref
cardinality: one-to-many
- target: product-path
cardinality: one-to-many
flow_bindings:
- { flow_node_ref: uf-orient-portfolio, access: reads }
- { flow_node_ref: uf-orient-portfolio, access: displays }
- { flow_node_ref: uf-explain-boundaries, access: reads }
- { flow_node_ref: uf-recover-diagnostics, access: transitions }
- id: portfolio-path-ref
name: PortfolioPathRef
kind: entity
attributes: [path_ref_id, path_id, scope_path, source_status, resolution_state, boundary_kind, diagnostic_refs]
relationships:
- target: product-path
cardinality: many-to-zero-or-one
- target: diagnostic-issue
cardinality: one-to-many
flow_bindings:
- { flow_node_ref: uf-orient-portfolio, access: displays }
- { flow_node_ref: uf-explain-boundaries, access: displays }
- { flow_node_ref: uf-recover-diagnostics, access: writes }
- id: product-path
name: ProductPath
kind: aggregate-root
attributes: [path_id, label, scope_path, source_status, pipeline_stage, reason, next_skill, revisit_trigger, last_touched, path_state, trust_level, handoff_eligibility, boundary_kind]
lifecycle_ref: product-path-lifecycle
relationships:
- target: source-artifact
cardinality: one-to-many
- target: source-backed-claim
cardinality: one-to-many
- target: provenance-record
cardinality: one-to-many
- target: diagnostic-issue
cardinality: one-to-many
- target: handoff-summary
cardinality: one-to-many
flow_bindings:
- { flow_node_ref: uf-orient-portfolio, access: displays }
- { flow_node_ref: uf-verify-selected-path, access: reads }
- { flow_node_ref: uf-verify-selected-path, access: writes }
- { flow_node_ref: uf-handoff-resume, access: reads }
- { flow_node_ref: uf-explain-boundaries, access: reads }
- { flow_node_ref: uf-explain-boundaries, access: displays }
- id: source-artifact
name: SourceArtifact
kind: entity
attributes: [artifact_id, artifact_ref, artifact_kind, source_role, read_status, approval_status, freshness_state, parser_confidence, source_confidence, warnings]
lifecycle_ref: source-artifact-lifecycle
relationships:
- target: source-backed-claim
cardinality: many-to-many
- target: provenance-record
cardinality: many-to-many
- target: diagnostic-issue
cardinality: one-to-many
flow_bindings:
- { flow_node_ref: uf-verify-selected-path, access: reads }
- { flow_node_ref: uf-inspect-source-provenance, access: reads }
- { flow_node_ref: uf-inspect-source-provenance, access: displays }
- { flow_node_ref: uf-recover-diagnostics, access: transitions }
- id: source-backed-claim
name: SourceBackedClaim
kind: entity
attributes: [claim_id, path_id, claim_kind, claim_text, support_status, parser_confidence, source_confidence, trust_level, derived_from, warnings]
relationships:
- target: evidence-link
cardinality: one-to-zero-or-many
- target: diagnostic-issue
cardinality: one-to-many
flow_bindings:
- { flow_node_ref: uf-verify-selected-path, access: displays }
- { flow_node_ref: uf-inspect-source-provenance, access: reads }
- { flow_node_ref: uf-inspect-source-provenance, access: displays }
- { flow_node_ref: uf-recover-diagnostics, access: reads }
- { flow_node_ref: uf-handoff-resume, access: reads }
- id: evidence-link
name: EvidenceLink
kind: entity
attributes: [evidence_link_id, claim_id, evidence_ref, resolved_ref, resolution_state, support_status, read_status, source_hint, warnings]
lifecycle_ref: evidence-link-lifecycle
relationships:
- target: source-artifact
cardinality: many-to-zero-or-one
flow_bindings:
- { flow_node_ref: uf-inspect-source-provenance, access: triggers }
- { flow_node_ref: uf-inspect-source-provenance, access: displays }
- { flow_node_ref: uf-recover-diagnostics, access: transitions }
- id: provenance-record
name: ProvenanceRecord
kind: entity
attributes: [provenance_id, path_id, provenance_ref, provenance_kind, approval_state, decision_summary, canonical_target, archive_refs, confidence, warnings]
lifecycle_ref: provenance-record-lifecycle
relationships:
- target: source-artifact
cardinality: many-to-many
flow_bindings:
- { flow_node_ref: uf-inspect-source-provenance, access: reads }
- { flow_node_ref: uf-inspect-source-provenance, access: displays }
- { flow_node_ref: uf-recover-diagnostics, access: reads }
- { flow_node_ref: uf-handoff-resume, access: reads }
- id: diagnostic-issue
name: DiagnosticIssue
kind: entity
attributes: [issue_id, issue_kind, severity, level, state, affected_refs, affected_claims, affected_paths, handoff_impact, recommended_manual_check]
lifecycle_ref: diagnostic-issue-lifecycle
flow_bindings:
- { flow_node_ref: uf-recover-diagnostics, access: writes }
- { flow_node_ref: uf-recover-diagnostics, access: displays }
- { flow_node_ref: uf-recover-diagnostics, access: transitions }
- { flow_node_ref: uf-orient-portfolio, access: displays }
- { flow_node_ref: uf-handoff-resume, access: reads }
- id: handoff-summary
name: HandoffSummary
kind: entity
attributes: [summary_id, path_id, handoff_type, copy_state, confidence, next_skill, branch_state_explanation, handoff_boundary, included_evidence_refs, included_provenance_refs, included_diagnostic_refs, blocked_reasons]
lifecycle_ref: handoff-summary-lifecycle
relationships:
- target: evidence-link
cardinality: many-to-many
- target: provenance-record
cardinality: many-to-many
- target: diagnostic-issue
cardinality: many-to-many
flow_bindings:
- { flow_node_ref: uf-handoff-resume, access: triggers }
- { flow_node_ref: uf-handoff-resume, access: writes }
- { flow_node_ref: uf-handoff-resume, access: displays }
- { flow_node_ref: uf-explain-boundaries, access: displays }
state_machines:
- id: portfolio-snapshot-lifecycle
subject: PortfolioSnapshot
states:
- { name: unscanned, maps_to_ui_state: "Empty initial portfolio frame" }
- { name: scanning, maps_to_ui_state: "Loading portfolio overview" }
- { name: no-afps-state, maps_to_ui_state: "Empty/no-manifest state" }
- { name: manifest-malformed, maps_to_ui_state: "Blocking parse diagnostic" }
- { name: paths-discovered, maps_to_ui_state: "Grouped portfolio rows" }
- { name: partial, maps_to_ui_state: "Visible warning/partial badges" }
- { name: source-backed, maps_to_ui_state: "Clean portfolio overview" }
- { name: blocked, maps_to_ui_state: "Blocking portfolio diagnostic" }
transitions:
- { from: unscanned, event: ScanPortfolio, to: scanning }
- { from: scanning, event: ProgressManifestMissing, to: no-afps-state }
- { from: scanning, event: ProgressManifestMalformed, to: manifest-malformed }
- { from: scanning, event: ProductPathRecordDiscovered, to: paths-discovered }
- { from: paths-discovered, event: DiagnosticIssueRaised, guard: non_blocking_portfolio_issue, to: partial }
- { from: paths-discovered, event: DiagnosticIssueClassified, guard: no_blocking_issues, to: source-backed }
- { from: partial, event: DiagnosticIssueClassified, guard: blocking_portfolio_issue, to: blocked }
- id: product-path-lifecycle
subject: ProductPath
states:
- { name: undiscovered, maps_to_ui_state: "Unresolved active ref or absent row" }
- { name: discovered, maps_to_ui_state: "Portfolio row" }
- { name: selected, maps_to_ui_state: "Selected path loading frame" }
- { name: source-inventorying, maps_to_ui_state: "Source checks loading" }
- { name: source-backed, maps_to_ui_state: "Clean selected-path detail" }
- { name: partial, maps_to_ui_state: "Partial detail with unresolved refs" }
- { name: warning, maps_to_ui_state: "Warning detail and handoff caveats" }
- { name: blocked, maps_to_ui_state: "Blocked detail and diagnostics" }
- { name: inactive, maps_to_ui_state: "Inactive path explanation" }
- { name: promoted-boundary, maps_to_ui_state: "Promoted boundary explanation" }
- { name: out-of-scope, maps_to_ui_state: "Out-of-scope explanation" }
transitions:
- { from: undiscovered, event: ProductPathRecordDiscovered, to: discovered }
- { from: discovered, event: ProductPathRecordExcluded, to: inactive }
- { from: discovered, event: PromotedBoundaryDetected, to: promoted-boundary }
- { from: discovered, event: ProductPathSelected, to: selected }
- { from: selected, event: ScopedSourceInventoryStarted, to: source-inventorying }
- { from: source-inventorying, event: ClaimSupportEvaluated, guard: required_claims_supported, to: source-backed }
- { from: source-inventorying, event: EvidenceRefUnresolved, guard: non_core_support_missing, to: partial }
- { from: source-backed, event: DiagnosticIssueRaised, guard: non_blocking_caveat, to: warning }
- { from: warning, event: DiagnosticIssueClassified, guard: blocking_issue, to: blocked }
- id: source-artifact-lifecycle
subject: SourceArtifact
states: [referenced, resolving, found, parsed, confirmed, review-only, amended, archived, missing, unreadable, malformed, stale, unsupported]
transitions:
- { from: referenced, event: ScopedSourceInventoryStarted, to: resolving }
- { from: resolving, event: ScopedResearchArtifactResolved, to: found }
- { from: resolving, event: ScopedResearchArtifactMissing, to: missing }
- { from: found, event: ProgressManifestParsed, to: parsed }
- { from: parsed, event: ProvenanceRecordConfirmed, to: confirmed }
- { from: parsed, event: ProvenanceRecordReviewOnly, to: review-only }
- id: evidence-link-lifecycle
subject: EvidenceLink
states: [unresolved, resolving, resolved, supports-claim, partially-supports-claim, contradicts-claim, inaccessible]
transitions:
- { from: unresolved, event: EvidenceRefResolutionStarted, to: resolving }
- { from: resolving, event: EvidenceRefResolved, to: resolved }
- { from: resolved, event: ClaimSupportEvaluated, guard: supports, to: supports-claim }
- { from: resolved, event: ClaimSupportEvaluated, guard: partial_support, to: partially-supports-claim }
- { from: resolved, event: ClaimSupportEvaluated, guard: contradiction, to: contradicts-claim }
- id: provenance-record-lifecycle
subject: ProvenanceRecord
states: [missing, discovered, review, confirmed, amended, archived, partial, contradicted]
transitions:
- { from: missing, event: ProvenanceRecordDiscovered, to: discovered }
- { from: discovered, event: ProvenanceRecordConfirmed, to: confirmed }
- { from: discovered, event: ProvenanceRecordReviewOnly, to: review }
- { from: confirmed, event: AmendmentDetected, to: amended }
- id: diagnostic-issue-lifecycle
subject: DiagnosticIssue
states: [none, raised, classified, warning, blocked, partial, resolved-by-source-review]
transitions:
- { from: none, event: DiagnosticIssueRaised, to: raised }
- { from: raised, event: DiagnosticIssueClassified, to: classified }
- { from: classified, event: SeverityPolicyApplied, guard: non_blocking, to: warning }
- { from: classified, event: SeverityPolicyApplied, guard: blocking, to: blocked }
- { from: classified, event: SeverityPolicyApplied, guard: incomplete, to: partial }
- id: handoff-summary-lifecycle
subject: HandoffSummary
states: [unavailable, requested, clean-copy, warning-copy, blocked, explanatory, promoted-boundary, out-of-scope, copied, suppressed]
transitions:
- { from: unavailable, event: HandoffGenerationRequested, to: requested }
- { from: requested, event: CleanCopyPrepared, to: clean-copy }
- { from: requested, event: WarningCopyPrepared, to: warning-copy }
- { from: requested, event: HandoffBlocked, to: blocked }
- { from: requested, event: InactivePathExplained, to: explanatory }
- { from: requested, event: PromotedBoundaryDetected, to: promoted-boundary }
- { from: requested, event: OutOfScopeRequestExplained, to: out-of-scope }
events:
- { id: repository-scan-started, name: RepositoryScanStarted, flow_bindings: [{ flow_node_ref: uf-orient-portfolio, access: triggers }] }
- { id: product-path-selected, name: ProductPathSelected, flow_bindings: [{ flow_node_ref: uf-orient-portfolio, access: triggers }, { flow_node_ref: uf-verify-selected-path, access: writes }] }
- { id: claim-support-evaluated, name: ClaimSupportEvaluated, flow_bindings: [{ flow_node_ref: uf-inspect-source-provenance, access: reads }, { flow_node_ref: uf-recover-diagnostics, access: reads }] }
- { id: diagnostic-issue-raised, name: DiagnosticIssueRaised, flow_bindings: [{ flow_node_ref: uf-recover-diagnostics, access: writes }] }
- { id: handoff-summary-prepared, name: HandoffSummaryPrepared, flow_bindings: [{ flow_node_ref: uf-handoff-resume, access: displays }] }
- { id: boundary-explanation-requested, name: BoundaryExplanationRequested, flow_bindings: [{ flow_node_ref: uf-explain-boundaries, access: triggers }] }
commands:
- { id: scan-portfolio, name: ScanPortfolio, flow_bindings: [{ flow_node_ref: uf-orient-portfolio, access: triggers }] }
- { id: select-product-path, name: SelectProductPath, flow_bindings: [{ flow_node_ref: uf-orient-portfolio, access: triggers }, { flow_node_ref: uf-verify-selected-path, access: writes }] }
- { id: evaluate-diagnostics, name: EvaluateDiagnostics, flow_bindings: [{ flow_node_ref: uf-recover-diagnostics, access: writes }] }
- { id: generate-handoff, name: GenerateHandoff, flow_bindings: [{ flow_node_ref: uf-handoff-resume, access: triggers }, { flow_node_ref: uf-explain-boundaries, access: triggers }] }
- { id: copy-handoff, name: CopyHandoff, flow_bindings: [{ flow_node_ref: uf-handoff-resume, access: writes }] }
read_models:
- { id: portfolio-overview, name: Portfolio Overview, flow_bindings: [{ flow_node_ref: uf-orient-portfolio, access: displays }] }
- { id: selected-path-detail, name: Selected Path Detail, flow_bindings: [{ flow_node_ref: uf-verify-selected-path, access: displays }] }
- { id: evidence-inspector, name: Evidence Inspector, flow_bindings: [{ flow_node_ref: uf-inspect-source-provenance, access: displays }] }
- { id: provenance-timeline, name: Provenance Timeline, flow_bindings: [{ flow_node_ref: uf-inspect-source-provenance, access: displays }] }
- { id: diagnostics-panel, name: Diagnostics Panel, flow_bindings: [{ flow_node_ref: uf-recover-diagnostics, access: displays }] }
- { id: handoff-summary, name: Handoff Summary, flow_bindings: [{ flow_node_ref: uf-handoff-resume, access: displays }, { flow_node_ref: uf-explain-boundaries, access: displays }] }
policies:
- { id: source-backed-display-rule, name: Source-backed display rule, flow_bindings: [{ flow_node_ref: uf-verify-selected-path, access: reads }, { flow_node_ref: uf-inspect-source-provenance, access: reads }] }
- { id: unresolved-ref-preservation-rule, name: Unresolved ref preservation rule, flow_bindings: [{ flow_node_ref: uf-orient-portfolio, access: displays }, { flow_node_ref: uf-recover-diagnostics, access: transitions }] }
- { id: boundary-suppression-rule, name: Boundary suppression rule, flow_bindings: [{ flow_node_ref: uf-explain-boundaries, access: displays }, { flow_node_ref: uf-handoff-resume, access: displays }] }
- { id: confidence-separation-rule, name: Confidence separation rule, flow_bindings: [{ flow_node_ref: uf-inspect-source-provenance, access: reads }, { flow_node_ref: uf-recover-diagnostics, access: reads }] }
- { id: warning-preservation-rule, name: Warning preservation rule, flow_bindings: [{ flow_node_ref: uf-handoff-resume, access: displays }] }
contracts:
- id: scan-portfolio-contract
name: ScanPortfolio
kind: command
request_shape: [repository_context, include_inactive, include_diagnostics, source_refs]
response_shape: [scan_state, repository_ref, portfolio_snapshot_id, source_health, path_counts, active_path_refs, excluded_path_refs, unresolved_path_refs, diagnostics, read_models_updated]
error_shape: [no-afps-state, progress-manifest-missing, progress-manifest-malformed, permission-denied, scan-partial, source-unreadable]
flow_bindings:
- { flow_node_ref: uf-orient-portfolio, access: triggers }
- { flow_node_ref: uf-recover-diagnostics, access: transitions }
- id: selected-path-detail-contract
name: GetSelectedPathDetail
kind: query
request_shape: [selector, include_claims, include_refs, include_diagnostics]
response_shape: [path_id, label, scope_path, source_status, path_state, pipeline_stage, reason, next_skill, trust, claims, evidence_refs, provenance_refs, diagnostics, handoff_eligibility, available_actions, blocked_actions]
error_shape: [path-not-selected, path-not-found, source-artifact-missing, path-detail-partial, path-detail-blocked, path-state-contradicted]
flow_bindings:
- { flow_node_ref: uf-verify-selected-path, access: reads }
- { flow_node_ref: uf-verify-selected-path, access: displays }
- id: inspect-evidence-ref-contract
name: InspectEvidenceRef
kind: query
request_shape: [claim_ref, evidence_ref, path_id, source_hint, preview_requested]
response_shape: [claim_id, claim_text, evidence_link_id, evidence_ref, resolved_ref, resolution_state, read_status, support_status, source_hint, trust, diagnostics, preview_available]
error_shape: [claim-not-found, evidence-ref-missing, evidence-ref-unresolved, source-preview-unavailable, evidence-inaccessible, claim-contradicted]
flow_bindings:
- { flow_node_ref: uf-inspect-source-provenance, access: displays }
- { flow_node_ref: uf-recover-diagnostics, access: transitions }
- id: generate-handoff-contract
name: GenerateHandoff
kind: command
request_shape: [selector, handoff_intent, detail_level, include_refs, include_diagnostics]
response_shape: [summary_id, handoff_type, handoff_state, path_id, state, pipeline_stage, next_skill, branch_state_explanation, confidence, evidence_refs, provenance_refs, warnings, blocked_reasons, boundary, copy_allowed, normal_next_command_suppressed]
error_shape: [handoff-unavailable, blocking-diagnostics-present, next-skill-unsupported, boundary-explanation-required, source-support-insufficient, out-of-scope-request]
flow_bindings:
- { flow_node_ref: uf-handoff-resume, access: triggers }
- { flow_node_ref: uf-explain-boundaries, access: displays }
Flow Binding Matrix
| Flow node | Reads | Writes or triggers | Displays | Transitions |
|---|---|---|---|---|
uf-orient-portfolio | RepositoryContext, PortfolioSnapshot, PortfolioPathRef, ProductPath, SourceArtifact, DiagnosticIssue | ScanPortfolio, SelectProductPath, scan state, selected path target | Portfolio Overview, grouped path rows, source health, active/parallel/inactive/unresolved states | unscanned, scanning, no-AFPS, malformed, paths-discovered, partial, source-backed, blocked |
uf-verify-selected-path | ProductPath, SourceArtifact, SourceBackedClaim, EvidenceLink, ProvenanceRecord, TrustEnvelope | Selected-path context and source inventory | Selected Path Detail with source-backed, inferred, partial, warning, and blocked labels | selected, source-inventorying, source-backed, partial, warning, blocked |
uf-inspect-source-provenance | SourceBackedClaim, EvidenceLink, SourceArtifact, ProvenanceRecord | Evidence resolution, provenance classification, diagnostic re-evaluation when trust changes | Evidence Inspector, Provenance Timeline, resolved/unresolved/support/contradiction states | resolved, supports-claim, partially-supports-claim, contradicted, confirmed, review-only, missing |
uf-recover-diagnostics | DiagnosticIssue, affected source/claim/path/portfolio/handoff refs | EvaluateDiagnostics, severity, affected level, handoff impact, trust degradation | Diagnostics Panel, warning/blocking/partial groups, recommended manual checks | raised, classified, warning, blocked, partial, resolved-by-source-review |
uf-handoff-resume | ProductPath, SourceBackedClaim, EvidenceLink, ProvenanceRecord, DiagnosticIssue, HandoffSummary | GenerateHandoff, CopyHandoff, copy state, included refs, blocked reasons | Handoff Summary, clean/warning/blocked copy state, suppression reasons | requested, clean-copy, warning-copy, blocked, copied, suppressed |
uf-explain-boundaries | PortfolioPathRef, ProductPath, BoundaryKind, HandoffSummary, source rationale | ExplainBoundary, boundary handoff generation, next-command suppression | Inactive, promoted-boundary, out-of-scope, unsupported, unresolved explanation | inactive, promoted-boundary, out-of-scope, explanatory, suppressed |
Visual Model
Logical ERD
| Relationship | Cardinality |
|---|---|
| RepositoryContext to PortfolioSnapshot | one to zero-or-one |
| PortfolioSnapshot to ProductPath | one to many |
| ProductPath to SourceBackedClaim | one to many |
| SourceBackedClaim to EvidenceLink | one to zero-or-many |
| ProductPath to HandoffSummary | one to many over time |
Product Path Lifecycle
| State family | Handoff effect |
|---|---|
| source-backed | Eligible for clean copy when diagnostics are non-blocking. |
| partial or warning | Eligible only when warnings remain visible and are included. |
| blocked | Suppresses clean handoff and may suppress all next-command output. |
| inactive/promoted/out-of-scope | Produces explanatory or boundary handoff instead of normal route. |
Assumptions And Deferrals
| Item | Status | Decision impact |
|---|---|---|
| ProductPath is the main aggregate root. | High confidence | All five framework passes converged here. |
| PortfolioSnapshot is a thin aggregate for orientation. | Medium confidence | Needed for grouped paths and unresolved refs, but should not own selected-path trust. |
| Parser confidence and source confidence remain separate. | High confidence | Prevents a confident parser from masking weak source support, or vice versa. |
| Key-moments is still pending. | Known caveat | This model covers the approved first read-through and can be narrowed through modify-back later. |
| Storage, endpoints, auth, migrations, indexes, deployment, file watchers, parser implementation, and UI layout. | Deferred | Owned by `$spec-interview` or later implementation work, not this logical model. |
Confirmed File Changes
| Path | Confirmed action | Notes |
|---|---|---|
design/afps-tracker/domain-model-afps-tracker.md | Created canonical model doc | Matches confirmed content rendered above. |
design/afps-tracker/model-tree-afps-tracker.yaml | Created canonical model-tree manifest | Matches confirmed content rendered above with status confirmed. |
design/afps-tracker/flow-tree-afps-tracker.yaml | Updated after approval | Attach branches[].model_ref for the modeled branches and optional top-level model_tree_ref. Do not touch route. |
research/afps-tracker/glossary.md | Appended approved glossary terms | Glossary gate approved additions. |
docs/history/archive/YYYY-MM-DD/HHMMSS/... | Archived working cursor after canonical write | Archived under docs/history/archive/2026-07-02/002008/. |
Approval Record
alignment_status: confirmed
confirmation_date: 2026-07-02
approval_source: final compiled response YAML pasted into $state-model afps-tracker.
This page is current for the completed alignment cycle. Later research may amend it only by archiving this confirmed page and highlighting the amendment.
| Gate | Decision | Target path | Notes |
|---|---|---|---|
| Domain model | approve | design/afps-tracker/domain-model-afps-tracker.md | No notes. |
| Model tree manifest | approve | design/afps-tracker/model-tree-afps-tracker.yaml | No notes. |
| Flow-tree model attachment | approve | design/afps-tracker/flow-tree-afps-tracker.yaml | No notes. |
| Glossary additions | approve | research/afps-tracker/glossary.md | No notes. |
# Invoke with: $state-model afps-tracker
command: "$state-model afps-tracker"
alignment_page: alignment/state-model-afps-tracker.html
response_status: complete
approval_status: ready-for-agent-review
required_gate_status: complete
unanswered_required_questions: []
gate_answers:
- section: "Domain model"
gate_type: "approval"
status: "answered"
answer: "approve"
notes: ""
target_path: "design/afps-tracker/domain-model-afps-tracker.md"
- section: "Model tree manifest"
gate_type: "approval"
status: "answered"
answer: "approve"
notes: ""
target_path: "design/afps-tracker/model-tree-afps-tracker.yaml"
- section: "Flow-tree model attachment"
gate_type: "approval"
status: "answered"
answer: "approve"
notes: ""
target_path: "design/afps-tracker/flow-tree-afps-tracker.yaml"
- section: "Glossary additions"
gate_type: "approval"
status: "answered"
answer: "approve"
notes: ""
target_path: "research/afps-tracker/glossary.md"
agent_routing:
workflow: design-tree-loop
parent_skill: state-model
command: "$state-model afps-tracker"
topic: afps-tracker
product_path: research/afps-tracker
gate_owner: parent-orchestrator
gate_type: state-model-alignment
next_resolution: parent-resolves-from-yaml-and-filesystem