Files
wehub-resource-sync f99010fae1
CI / lint (push) Failing after 1s
CI / frontend (push) Failing after 1s
CI / scripts (push) Failing after 1s
CI / Go Test (ubuntu-latest) (push) Failing after 0s
CI / frontend-node-25 (push) Failing after 1s
CI / docs (push) Failing after 0s
CI / coverage (push) Failing after 0s
CI / e2e (push) Failing after 0s
Docker / build-and-push (push) Failing after 1s
CI / integration (push) Failing after 4m43s
CI / Go Test (windows-latest) (push) Has been cancelled
CI / Desktop Unit Tests (Windows) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Linux (arm64)) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Linux) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Windows) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (aarch64)) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (x86_64)) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:30:36 +08:00

304 lines
9.4 KiB
Go

package sync
import (
"errors"
"os"
"path/filepath"
"strings"
"go.kenn.io/agentsview/internal/db"
"go.kenn.io/agentsview/internal/parser"
)
// verifiedSourceSignature is the complete local filesystem identity trusted
// by the gate. Sidecar fields are zero for providers without an auxiliary
// source and carry the raw Codex index identity when that provider opts in.
type verifiedSourceSignature struct {
size int64
mtime int64
inode int64
device int64
changeTime int64
sidecarSize int64
sidecarMtime int64
sidecarInode int64
sidecarDevice int64
sidecarChangeTime int64
}
// verifiedSourceRecord deliberately combines trust, invalidation, and pass
// bookkeeping in one map value. Unknown-path invalidations also live here so a
// concurrent stale promotion cannot recreate trust after the event.
type verifiedSourceRecord struct {
signature verifiedSourceSignature
invalidationGen uint64
lastSeenPass uint64
trusted bool
}
// verifiedSourceCapture binds a pre-verification signature to the path's
// invalidation coordinates. Promotion succeeds only if neither coordinate
// changed while content verification was in flight.
type verifiedSourceCapture struct {
path string
signature verifiedSourceSignature
epoch uint64
invalidation uint64
}
// captureVerifiedSource records that a full pass saw path, snapshots its
// invalidation coordinates before verification, and reports whether the same
// signature was previously promoted.
func (e *Engine) captureVerifiedSource(
path string,
signature verifiedSourceSignature,
) (verifiedSourceCapture, bool) {
if path == "" {
return verifiedSourceCapture{}, false
}
e.verifiedSourceMu.Lock()
defer e.verifiedSourceMu.Unlock()
if e.verifiedSources == nil {
e.verifiedSources = make(map[string]verifiedSourceRecord)
}
record := e.verifiedSources[path]
if e.verifiedSourceActivePass != 0 {
record.lastSeenPass = e.verifiedSourceActivePass
}
e.verifiedSources[path] = record
return verifiedSourceCapture{
path: path,
signature: signature,
epoch: e.verifiedSourceEpoch,
invalidation: record.invalidationGen,
}, record.trusted && record.signature == signature
}
// promoteVerifiedSource trusts a capture only when no path invalidation,
// global clear, or completed-pass pruning landed after capture.
func (e *Engine) promoteVerifiedSource(capture verifiedSourceCapture) {
if capture.path == "" {
return
}
e.verifiedSourceMu.Lock()
defer e.verifiedSourceMu.Unlock()
record, ok := e.verifiedSources[capture.path]
if !ok ||
capture.epoch != e.verifiedSourceEpoch ||
capture.invalidation != record.invalidationGen {
return
}
record.signature = capture.signature
record.trusted = true
e.verifiedSources[capture.path] = record
}
// invalidateVerifiedSource drops one path's trust and advances its generation.
// During an active full pass the invalidation record is marked seen so pruning
// cannot erase the generation before a stale in-flight promotion observes it.
func (e *Engine) invalidateVerifiedSource(path string) {
if path == "" {
return
}
e.verifiedSourceMu.Lock()
defer e.verifiedSourceMu.Unlock()
if e.verifiedSources == nil {
e.verifiedSources = make(map[string]verifiedSourceRecord)
}
record := e.verifiedSources[path]
record.trusted = false
record.invalidationGen++
if e.verifiedSourceActivePass != 0 {
record.lastSeenPass = e.verifiedSourceActivePass
}
e.verifiedSources[path] = record
}
// clearVerifiedSources invalidates every trusted source and vetoes all
// captures taken before the clear without retaining per-path tombstones.
func (e *Engine) clearVerifiedSources() {
e.verifiedSourceMu.Lock()
defer e.verifiedSourceMu.Unlock()
e.verifiedSources = nil
e.verifiedSourceEpoch++
}
// beginVerifiedSourcePass starts the seen-generation used by one full source
// discovery. Changed-path and scoped passes do not call it and never prune.
func (e *Engine) beginVerifiedSourcePass() uint64 {
e.verifiedSourceMu.Lock()
defer e.verifiedSourceMu.Unlock()
e.verifiedSourcePass++
if e.verifiedSourcePass == 0 {
e.verifiedSourcePass++
}
e.verifiedSourceActivePass = e.verifiedSourcePass
return e.verifiedSourceActivePass
}
// finishVerifiedSourcePass prunes records absent from a completed full
// discovery. Incomplete or superseded passes leave existing trust untouched.
func (e *Engine) finishVerifiedSourcePass(pass uint64, complete bool) {
if pass == 0 {
return
}
e.verifiedSourceMu.Lock()
defer e.verifiedSourceMu.Unlock()
if e.verifiedSourceActivePass != pass {
return
}
if complete {
for path, record := range e.verifiedSources {
if record.lastSeenPass != pass {
delete(e.verifiedSources, path)
}
}
}
e.verifiedSourceActivePass = 0
}
// verifiedProviderSourceState captures the local stat/ctime signature before
// any content verification. Providers must opt in explicitly; forced,
// path-rewritten, non-regular, and unreliable-change-time sources bypass the
// gate and retain their existing verification path.
func (e *Engine) verifiedProviderSourceState(
provider parser.Provider,
source parser.SourceRef,
file parser.DiscoveredFile,
) (verifiedSourceCapture, int64, bool, bool) {
if e.forceParse || file.ForceParse || e.pathRewriter != nil ||
provider.Capabilities().Source.VerifiedLocalStat !=
parser.CapabilitySupported {
return verifiedSourceCapture{}, 0, false, false
}
path := providerDiscoveredPath(source)
if path == "" || strings.HasPrefix(path, "s3://") {
return verifiedSourceCapture{}, 0, false, false
}
path = filepath.Clean(path)
info, err := os.Stat(path)
if err != nil || !info.Mode().IsRegular() {
return verifiedSourceCapture{}, 0, false, false
}
changeTime, ok := fileChangeTime(path, info)
if !ok {
return verifiedSourceCapture{}, 0, false, false
}
inode, device := getFileIdentity(info)
mtime := info.ModTime().UnixNano()
sidecar := verifiedSourceSignature{}
if provider.Definition().Type == parser.AgentCodex {
indexPath := parser.CodexSessionIndexPath(path)
if indexPath != "" {
indexInfo, indexErr := os.Stat(indexPath)
switch {
case indexErr == nil:
if !indexInfo.Mode().IsRegular() {
return verifiedSourceCapture{}, 0, false, false
}
indexChangeTime, reliable := fileChangeTime(
indexPath, indexInfo,
)
if !reliable {
return verifiedSourceCapture{}, 0, false, false
}
indexInode, indexDevice := getFileIdentity(indexInfo)
sidecar.sidecarSize = indexInfo.Size()
sidecar.sidecarMtime = indexInfo.ModTime().UnixNano()
sidecar.sidecarInode = indexInode
sidecar.sidecarDevice = indexDevice
sidecar.sidecarChangeTime = indexChangeTime
case !errors.Is(indexErr, os.ErrNotExist):
return verifiedSourceCapture{}, 0, false, false
}
}
}
capture, fresh := e.captureVerifiedSource(path, verifiedSourceSignature{
size: info.Size(),
mtime: mtime,
inode: inode,
device: device,
changeTime: changeTime,
sidecarSize: sidecar.sidecarSize,
sidecarMtime: sidecar.sidecarMtime,
sidecarInode: sidecar.sidecarInode,
sidecarDevice: sidecar.sidecarDevice,
sidecarChangeTime: sidecar.sidecarChangeTime,
})
if sidecar.sidecarMtime > mtime {
mtime = sidecar.sidecarMtime
}
return capture, mtime, fresh, true
}
// verifiedProviderSourceFreshInDB preserves the self-healing checks that run
// below the verified-source fast path. A matching filesystem signature cannot
// hide a missing active row, forced file-metadata reset, old parser data
// version, or project value that the current parser knows how to repair.
func (e *Engine) verifiedProviderSourceFreshInDB(
source parser.SourceRef,
wantSize, wantMtime int64,
) bool {
path := providerDiscoveredPath(source)
if path == "" {
return false
}
project, dataVersion, storedSize, storedMtime, ok :=
e.db.GetSourceRepairStateByPath(path)
if !ok || parser.NeedsProjectReparse(project) {
return false
}
return storedSize == wantSize &&
storedMtime == wantMtime &&
dataVersion >= db.CurrentDataVersion()
}
func (e *Engine) verifiedLocalStatSupported(agent parser.AgentType) bool {
factory, ok := e.providerFactories[agent]
return ok && factory != nil &&
factory.Capabilities().Source.VerifiedLocalStat ==
parser.CapabilitySupported
}
func (e *Engine) markVerifiedSourceSeen(path string) {
if path == "" {
return
}
path = filepath.Clean(path)
e.verifiedSourceMu.Lock()
defer e.verifiedSourceMu.Unlock()
if e.verifiedSourceActivePass == 0 {
return
}
record, ok := e.verifiedSources[path]
if !ok {
return
}
record.lastSeenPass = e.verifiedSourceActivePass
e.verifiedSources[path] = record
}
// markVerifiedDiscoveredSources preserves trusted records for gateable sources
// present in the full pre-filter discovery set, including files omitted later
// by a quick-sync cutoff.
func (e *Engine) markVerifiedDiscoveredSources(files []parser.DiscoveredFile) {
if e.pathRewriter != nil {
return
}
for _, file := range files {
if e.verifiedLocalStatSupported(file.Agent) {
e.markVerifiedSourceSeen(file.Path)
}
}
}
// invalidateVerifiedDiscoveredSource makes a concrete changed-path signal win
// over a matching stat signature. Only opted-in local providers allocate
// invalidation records.
func (e *Engine) invalidateVerifiedDiscoveredSource(file parser.DiscoveredFile) {
if e.pathRewriter != nil || !e.verifiedLocalStatSupported(file.Agent) {
return
}
e.invalidateVerifiedSource(filepath.Clean(file.Path))
}