Documentation
¶
Overview ¶
Package intentgap mirrors the API's canonical payload_hash computation. The upload endpoint recomputes the hash server-side and rejects mismatches, so the CLI and API encoders must stay byte-compatible.
The golden fixture under testdata/payloadhash/ is copied from the API fixture, and the tests compare them when both repositories are present.
Index ¶
- Constants
- Variables
- func BuildAnalyzedBody(in AnalyzedBodyInput, producedAt time.Time) ([]byte, string, error)
- func BuildErroredBody(in UploadInput, reason, promptTemplateVersion string, producedAt time.Time) ([]byte, string, error)
- func BuildTransportOnlyBody(in UploadInput, producedAt time.Time) ([]byte, string, error)
- func ComputePayloadHash(in PayloadHashInput) (string, []byte, error)
- func IntentGapFindingSchema() (*jsonschema.Schema, error)
- func LoadOrCreateDeviceID() (string, error)
- func MapWriterNameToWire(writerName string) (string, bool)
- func ValidateFindings(findings json.RawMessage) error
- type AmbiguousPRError
- type AnalysisInput
- type AnalysisResult
- type AnalyzedBodyInput
- type Bundle
- type BundleAssembler
- type BundleCommit
- type BundleInput
- type BundleTruncation
- type BundleTurn
- type CiteOrDropResult
- type CommitMetaBetween
- type GitBundleAssembler
- type GitRepo
- type GitRepoOpener
- type InstalledProvider
- type IntentGapAnalyzer
- type LLMAnalyzer
- type LLMRunner
- type NoopTurnLoader
- type OpenPR
- type PayloadHashInput
- type ReasonCode
- type SkipReason
- type TurnLoader
- type UploadInput
- type UploadResponse
- type UploadResult
Constants ¶
const ( // MaxBundleDiffBytes caps the cumulative diff at 96 KiB. MaxBundleDiffBytes = 96 * 1024 // MaxBundleCommits caps the commit list sent to the analyzer. MaxBundleCommits = 100 // MaxBundleTurns caps captured prompts, retaining the most recent entries. MaxBundleTurns = 200 )
Bundle size limits keep analyzer input bounded.
const ( ProducerStateTransportOnly = "transport_only" ProducerStateAnalyzed = "analyzed" ProducerStateErrored = "errored" )
Producer states accepted by the intent-gap upload contract.
const ( AlgorithmVersionTransport = "0.1.0-transport" FindingSchemaVersion = "1" RedactionVersion = "1" )
Versions encoded in uploads and canonical payload hashes.
const AlgorithmVersionAnalyzed = "0.1.0-local-llm"
AlgorithmVersionAnalyzed identifies uploads produced by local LLM analysis.
const DeviceIDFileName = "device_id"
DeviceIDFileName is the basename of the file under AppConfigDir that holds the stable per-machine random device identifier.
const PromptTemplateVersion = "0.1.0"
PromptTemplateVersion identifies the prompt used to produce findings. Bump it when prompt changes may affect model output.
Variables ¶
var ( // error when no installed writer succeeded. ErrAnalyzerLLMUnavailable = errors.New("intentgap: no LLM CLI produced a response") // ErrAnalyzerParseFailed signals the LLM responded but neither // the original nor the retry response parsed into the expected // JSON shape. ErrAnalyzerParseFailed = errors.New("intentgap: could not parse findings JSON from LLM output") // ErrAnalyzerSchemaFailed signals the LLM output parsed as JSON // but failed schema validation. ErrAnalyzerSchemaFailed = errors.New("intentgap: LLM findings failed schema validation") // ErrAnalyzerInternal wraps unexpected analyzer-side errors // (cite-or-drop filter failure, coverage encode failure, etc.) // so the reason-code mapping has a stable sentinel to map to. ErrAnalyzerInternal = errors.New("intentgap: analyzer internal error") )
Analyzer errors map to stable upload reason codes.
var ( ErrNoOpenPR = errors.New("intentgap: no open PR for branch") ErrAmbiguousPR = errors.New("intentgap: multiple open PRs for branch") )
Discovery errors distinguish missing, ambiguous, and unavailable PR context.
ErrLineageUnavailable means lineage.db exists but cannot be read. Missing capture data is represented by an empty result instead.
var ErrNoInstalledProvider = errors.New("intentgap: no LLM CLI installed")
ErrNoInstalledProvider indicates that no supported local AI CLI is available.
var ErrRedactionFailed = errors.New("intentgap: redaction failed for at least one captured turn")
ErrRedactionFailed means a captured prompt could not be safely prepared for analysis. The analysis fails closed instead of omitting the prompt.
var ErrSkipped = errors.New("intentgap: upload skipped")
ErrSkipped marks a clean skip, such as a disabled setting or a branch with no open PR.
Functions ¶
func BuildAnalyzedBody ¶
BuildAnalyzedBody builds an analyzed request and its canonical hash.
func BuildErroredBody ¶
func BuildErroredBody(in UploadInput, reason, promptTemplateVersion string, producedAt time.Time) ([]byte, string, error)
BuildErroredBody builds an errored request with no findings.
Errored rows let the server's materializer keep showing the prior analyzed verdict on the check (it filters errored upstream) while dashboard surfaces still see the failure entry for diagnostics.
func BuildTransportOnlyBody ¶
BuildTransportOnlyBody builds the request body bytes plus the canonical payload hash for a transport-only upload. Pure: same input bytes always produce the same body bytes and the same hash, which is what the server's recompute relies on for idempotency.
func ComputePayloadHash ¶
func ComputePayloadHash(in PayloadHashInput) (string, []byte, error)
ComputePayloadHash returns the lowercase-hex sha256 of the canonical payload bytes and the bytes themselves.
func IntentGapFindingSchema ¶
func IntentGapFindingSchema() (*jsonschema.Schema, error)
IntentGapFindingSchema returns the compiled Draft 2020-12 schema for a single intent-gap finding. Compiled lazily on first use and cached for the process lifetime.
func LoadOrCreateDeviceID ¶
LoadOrCreateDeviceID returns a stable random identifier for this machine. The identifier is generated once on first call, persisted under the user-global Semantica config directory, and reused on every subsequent call. It is audit/context metadata only - the server intentionally excludes it from upload deduplication and from the canonical payload hash.
If the existing file is unreadable or holds an invalid UUID, a fresh one is generated and persisted in its place.
func MapWriterNameToWire ¶
MapWriterNameToWire returns the API provider value for a writer name.
func ValidateFindings ¶
func ValidateFindings(findings json.RawMessage) error
ValidateFindings checks each element of a findings array against the intent-gap finding schema. Empty / nil input is valid: an analyzed upload with no findings is a legitimate "no gaps found" result.
The CLI calls this before upload so schema violations surface locally with a clear field path; the server's identical validator is the second line of defense.
Types ¶
type AmbiguousPRError ¶
type AmbiguousPRError struct {
Matches []OpenPR
}
AmbiguousPRError contains all open PRs matching a branch.
func (*AmbiguousPRError) Error ¶
func (e *AmbiguousPRError) Error() string
func (*AmbiguousPRError) Is ¶
func (e *AmbiguousPRError) Is(target error) bool
type AnalysisInput ¶
AnalysisInput contains the repository, pull request, and local evidence used to derive findings. RepositoryID and PRNumber namespace finding IDs.
type AnalysisResult ¶
type AnalysisResult struct {
Findings json.RawMessage
CoverageSummary json.RawMessage
Provider string
Model string
PromptTemplateVersion string
}
AnalysisResult contains validated findings, coverage metadata, and the provider that produced the response.
type AnalyzedBodyInput ¶
type AnalyzedBodyInput struct {
UploadInput
PromptTemplateVersion string
Findings json.RawMessage
CoverageSummary json.RawMessage
}
AnalyzedBodyInput combines upload metadata with analyzer output.
type Bundle ¶
type Bundle struct {
RepoRoot string
BaseRef string
BaseSHA string
HeadSHA string
Commits []BundleCommit
// Diff is the cumulative unified diff, capped at MaxBundleDiffBytes.
Diff []byte
// Turns contains captured user prompts linked to commits in the PR range.
Turns []BundleTurn
Truncated BundleTruncation
}
Bundle contains the local changes and captured prompts analyzed for a PR.
type BundleAssembler ¶
type BundleAssembler interface {
Assemble(ctx context.Context, in BundleInput) (Bundle, error)
}
BundleAssembler builds analyzer input for a repository revision.
type BundleCommit ¶
BundleCommit identifies one commit in merge-base..HEAD.
type BundleInput ¶
type BundleInput struct {
RepoRoot string
// Base is the ref to diff against. Empty means "auto-detect".
Base string
HeadSHA string
}
BundleInput identifies the repository range to assemble.
type BundleTruncation ¶
BundleTruncation records input omitted by bundle size limits.
type BundleTurn ¶
type BundleTurn struct {
TurnID string
CommitHash string
TS int64
PromptExcerpt string
PromptExcerptHash string
}
BundleTurn is a captured user prompt associated with a commit. The analyzer correlates these prompts with the pull request diff to detect:
- under_impl: a turn whose intent was not fully implemented
- unrequested: a region whose intent does not match any captured turn
- deferred: a turn whose intent was added then removed (trajectory)
PromptExcerpt and PromptExcerptHash are verified citation anchors.
type CiteOrDropResult ¶
type CiteOrDropResult struct {
Findings json.RawMessage
AcceptedCount int
DroppedCount int
DroppedReasons map[string]int
}
CiteOrDropResult reports accepted findings and rejection reasons.
func FilterFindingsByCitations ¶
func FilterFindingsByCitations(findings json.RawMessage, bundle Bundle) (CiteOrDropResult, error)
FilterFindingsByCitations drops findings whose prompt or diff citations cannot be verified against the local bundle.
Prompt citations must match captured turns, and diff citations must match changed files and line ranges. Rejection counts are returned as metadata.
type CommitMetaBetween ¶
CommitMetaBetween describes a commit in the analyzed range.
type GitBundleAssembler ¶
type GitBundleAssembler struct {
// contains filtered or unexported fields
}
GitBundleAssembler combines Git history with captured prompts.
func NewGitBundleAssembler ¶
func NewGitBundleAssembler(opener GitRepoOpener, turns TurnLoader) *GitBundleAssembler
NewGitBundleAssembler constructs a Git-backed bundle assembler.
func (*GitBundleAssembler) Assemble ¶
func (a *GitBundleAssembler) Assemble(ctx context.Context, in BundleInput) (Bundle, error)
Assemble builds a bounded bundle for the requested revision range.
type GitRepo ¶
type GitRepo interface {
DefaultBaseRef(ctx context.Context) (string, error)
MergeBase(ctx context.Context, a, b string) (string, error)
DiffBetween(ctx context.Context, base, head string) ([]byte, error)
CommitSummariesBetween(ctx context.Context, base, head string, limit int) ([]CommitMetaBetween, error)
CountCommitsBetween(ctx context.Context, base, head string) (int, error)
}
GitRepo is the subset of git.Repo the assembler consumes.
type GitRepoOpener ¶
GitRepoOpener opens the Git operations required by bundle assembly.
type InstalledProvider ¶
InstalledProvider identifies the selected writer using API wire names.
func PickInstalledProvider ¶
func PickInstalledProvider(reg *llm.WriterRegistry) (InstalledProvider, error)
PickInstalledProvider returns the first installed, API-supported writer.
type IntentGapAnalyzer ¶
type IntentGapAnalyzer interface {
Analyze(ctx context.Context, in AnalysisInput) (AnalysisResult, error)
}
IntentGapAnalyzer produces findings for one pull request bundle.
type LLMAnalyzer ¶
type LLMAnalyzer struct {
// contains filtered or unexported fields
}
LLMAnalyzer runs the local provider fallback chain and validates its output.
func NewLLMAnalyzer ¶
func NewLLMAnalyzer(runner LLMRunner) *LLMAnalyzer
NewLLMAnalyzer constructs an analyzer backed by runner.
func (*LLMAnalyzer) Analyze ¶
func (a *LLMAnalyzer) Analyze(ctx context.Context, in AnalysisInput) (AnalysisResult, error)
Analyze produces validated findings. It retries once when the first response cannot be parsed as JSON.
type LLMRunner ¶
type LLMRunner interface {
GenerateText(ctx context.Context, prompt string) (*llm.GenerateTextResult, error)
}
LLMRunner is the analyzer's interface to the local provider registry.
type NoopTurnLoader ¶
type NoopTurnLoader struct{}
NoopTurnLoader returns no captured turns.
func (NoopTurnLoader) LoadTurnsForCommits ¶
func (NoopTurnLoader) LoadTurnsForCommits(context.Context, []string) ([]BundleTurn, error)
type OpenPR ¶
type OpenPR struct {
PRNumber int32 `json:"pr_number"`
State string `json:"state"`
Title string `json:"title,omitempty"`
HeadSHA string `json:"head_sha,omitempty"`
HeadBranch string `json:"head_branch,omitempty"`
BaseBranch string `json:"base_branch,omitempty"`
IsDraft bool `json:"is_draft"`
}
OpenPR contains the PR fields needed for local analysis.
func LookupOpenPRByBranch ¶
func LookupOpenPRByBranch( ctx context.Context, httpClient *http.Client, endpoint, token, repoID, branch string, ) (*OpenPR, error)
LookupOpenPRByBranch returns the single open PR for a short branch name. It uses http.DefaultClient when httpClient is nil and applies a 10-second timeout.
type PayloadHashInput ¶
type PayloadHashInput struct {
RepositoryID string
PRNumber int32
HeadSHA string
BaseSHA string
AlgorithmVersion string
PromptTemplateVersion string
FindingSchemaVersion string
RedactionVersion string
Provider string
Model string
ProducerState string
// CoverageSummary and Findings carry raw JSON. nil or empty input
// is treated as {} / [] so callers that omit the fields hash the
// same as ones that send empty literals.
CoverageSummary json.RawMessage
Findings json.RawMessage
}
PayloadHashInput contains the fields included in the canonical upload hash.
type ReasonCode ¶
type ReasonCode string
ReasonCode is a sanitized failure label suitable for upload. Detailed errors remain in the local activity log.
const ( ReasonBundleFailed ReasonCode = "bundle_failed" ReasonRedactionFailed ReasonCode = "redaction_failed" ReasonParseFailed ReasonCode = "parse_failed" ReasonSchemaFailed ReasonCode = "schema_failed" ReasonAnalyzerInternal ReasonCode = "analyzer_internal" )
func ReasonCodeFor ¶
func ReasonCodeFor(err error) ReasonCode
ReasonCodeFor maps an error to a stable upload label.
type SkipReason ¶
type SkipReason struct {
Reason string
}
SkipReason wraps a skip cause with the ErrSkipped sentinel so errors.Is(err, ErrSkipped) holds for any skip outcome.
func (*SkipReason) Error ¶
func (e *SkipReason) Error() string
func (*SkipReason) Is ¶
func (e *SkipReason) Is(target error) bool
type TurnLoader ¶
type TurnLoader interface {
LoadTurnsForCommits(ctx context.Context, commitHashes []string) ([]BundleTurn, error)
}
TurnLoader loads captured turns for a chronological list of commits.
type UploadInput ¶
type UploadInput struct {
RepositoryID string
PRNumber int32
HeadSHA string
BaseSHA string
Provider string
Model string
ProducerDeviceID string
}
UploadInput contains repository and producer metadata for an upload.
type UploadResponse ¶
type UploadResponse struct {
UploadID string `json:"upload_id"`
ReceivedAt string `json:"received_at"`
}
UploadResponse is the API's success body for both 201 (fresh) and 200 (idempotent duplicate).
type UploadResult ¶
UploadResult contains the accepted upload identity and HTTP status.
func PostUpload ¶
func PostUpload( ctx context.Context, httpClient *http.Client, endpoint, token string, in UploadInput, body []byte, idempotencyKey string, ) (*UploadResult, error)
PostUpload sends a prepared body. Both fresh and duplicate responses are successful outcomes.