@rkarim08/sia 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +35 -0
- package/.claude-plugin/plugin.json +27 -0
- package/.mcp.json +13 -0
- package/CLAUDE.md +226 -0
- package/LICENSE +202 -0
- package/PLUGIN_README.md +253 -0
- package/README.md +1013 -0
- package/agents/sia-changelog-writer.md +89 -0
- package/agents/sia-code-reviewer.md +86 -0
- package/agents/sia-conflict-resolver.md +100 -0
- package/agents/sia-convention-enforcer.md +69 -0
- package/agents/sia-debug.md +106 -0
- package/agents/sia-decision-reviewer.md +101 -0
- package/agents/sia-dependency-tracker.md +80 -0
- package/agents/sia-explain.md +126 -0
- package/agents/sia-feature.md +116 -0
- package/agents/sia-knowledge-capture.md +117 -0
- package/agents/sia-lead-architecture-advisor.md +93 -0
- package/agents/sia-lead-team-health.md +107 -0
- package/agents/sia-migration.md +100 -0
- package/agents/sia-onboarding.md +115 -0
- package/agents/sia-orientation.md +99 -0
- package/agents/sia-pm-briefing.md +106 -0
- package/agents/sia-pm-risk-advisor.md +82 -0
- package/agents/sia-qa-analyst.md +116 -0
- package/agents/sia-qa-regression-map.md +94 -0
- package/agents/sia-refactor.md +115 -0
- package/agents/sia-regression.md +112 -0
- package/agents/sia-security-audit.md +125 -0
- package/agents/sia-test-advisor.md +91 -0
- package/hooks/hooks.json +98 -0
- package/migrations/bridge/001_initial.sql +34 -0
- package/migrations/episodic/001_initial.sql +35 -0
- package/migrations/meta/001_initial.sql +68 -0
- package/migrations/semantic/001_initial.sql +292 -0
- package/migrations/semantic/002_ontology.sql +89 -0
- package/migrations/semantic/003_freshness.sql +63 -0
- package/migrations/semantic/004_v5_unified_schema.sql +194 -0
- package/migrations/semantic/005_backfill_event_kinds.sql +8 -0
- package/migrations/semantic/006_tree_sitter.sql +6 -0
- package/migrations/semantic/007_branch_snapshots.sql +22 -0
- package/package.json +110 -0
- package/scripts/branch-switch.sh +13 -0
- package/scripts/build-wasm-grammars.sh +81 -0
- package/scripts/post-compact.sh +8 -0
- package/scripts/post-tool-use.sh +10 -0
- package/scripts/pre-compact.sh +8 -0
- package/scripts/session-end.sh +8 -0
- package/scripts/session-start.sh +8 -0
- package/scripts/start-mcp.ts +45 -0
- package/scripts/stop-hook.sh +8 -0
- package/scripts/user-prompt-submit.sh +8 -0
- package/scripts/viz-server.ts +152 -0
- package/skills/sia-brainstorm/SKILL.md +156 -0
- package/skills/sia-brainstorm/scripts/frame-template.html +214 -0
- package/skills/sia-brainstorm/scripts/helper.js +95 -0
- package/skills/sia-brainstorm/scripts/server.cjs +338 -0
- package/skills/sia-brainstorm/scripts/start-server.sh +153 -0
- package/skills/sia-brainstorm/scripts/stop-server.sh +55 -0
- package/skills/sia-brainstorm/spec-document-reviewer-prompt.md +49 -0
- package/skills/sia-brainstorm/visual-companion.md +286 -0
- package/skills/sia-capture/SKILL.md +64 -0
- package/skills/sia-compare/SKILL.md +33 -0
- package/skills/sia-conflicts/SKILL.md +38 -0
- package/skills/sia-debug-workflow/SKILL.md +120 -0
- package/skills/sia-debug-workflow/root-cause-tracing.md +70 -0
- package/skills/sia-debug-workflow/scripts/find-polluter.sh +64 -0
- package/skills/sia-debug-workflow/temporal-investigation.md +72 -0
- package/skills/sia-digest/SKILL.md +23 -0
- package/skills/sia-dispatch/SKILL.md +69 -0
- package/skills/sia-dispatch/agent-task-template.md +99 -0
- package/skills/sia-doctor/SKILL.md +39 -0
- package/skills/sia-execute/SKILL.md +70 -0
- package/skills/sia-execute-plan/SKILL.md +85 -0
- package/skills/sia-export-import/SKILL.md +49 -0
- package/skills/sia-export-knowledge/SKILL.md +46 -0
- package/skills/sia-finish/SKILL.md +100 -0
- package/skills/sia-finish/pr-summary-template.md +54 -0
- package/skills/sia-freshness/SKILL.md +38 -0
- package/skills/sia-history/SKILL.md +42 -0
- package/skills/sia-impact/SKILL.md +70 -0
- package/skills/sia-index/SKILL.md +54 -0
- package/skills/sia-install/SKILL.md +39 -0
- package/skills/sia-lead-compliance/SKILL.md +16 -0
- package/skills/sia-lead-drift-report/SKILL.md +16 -0
- package/skills/sia-lead-knowledge-map/SKILL.md +16 -0
- package/skills/sia-learn/SKILL.md +58 -0
- package/skills/sia-plan/SKILL.md +68 -0
- package/skills/sia-plan/plan-reviewer-prompt.md +63 -0
- package/skills/sia-playbooks/SKILL.md +29 -0
- package/skills/sia-playbooks/reference-feature.md +100 -0
- package/skills/sia-playbooks/reference-flagging.md +50 -0
- package/skills/sia-playbooks/reference-orientation.md +92 -0
- package/skills/sia-playbooks/reference-regression.md +115 -0
- package/skills/sia-playbooks/reference-review.md +64 -0
- package/skills/sia-playbooks/reference-tools.md +239 -0
- package/skills/sia-pm-decision-log/SKILL.md +28 -0
- package/skills/sia-pm-risk-dashboard/SKILL.md +24 -0
- package/skills/sia-pm-sprint-summary/SKILL.md +27 -0
- package/skills/sia-prune/SKILL.md +45 -0
- package/skills/sia-qa-coverage/SKILL.md +28 -0
- package/skills/sia-qa-flaky/SKILL.md +20 -0
- package/skills/sia-qa-report/SKILL.md +26 -0
- package/skills/sia-reindex/SKILL.md +30 -0
- package/skills/sia-review-respond/SKILL.md +88 -0
- package/skills/sia-review-respond/pushback-patterns.md +90 -0
- package/skills/sia-search/SKILL.md +47 -0
- package/skills/sia-setup/SKILL.md +82 -0
- package/skills/sia-setup/setup-checklist.md +97 -0
- package/skills/sia-stats/SKILL.md +36 -0
- package/skills/sia-status/SKILL.md +44 -0
- package/skills/sia-sync/SKILL.md +46 -0
- package/skills/sia-team/SKILL.md +64 -0
- package/skills/sia-test/SKILL.md +92 -0
- package/skills/sia-test/testing-anti-patterns.md +104 -0
- package/skills/sia-tour/SKILL.md +29 -0
- package/skills/sia-upgrade/SKILL.md +43 -0
- package/skills/sia-verify/SKILL.md +81 -0
- package/skills/sia-visualize/SKILL.md +28 -0
- package/skills/sia-visualize-live/SKILL.md +55 -0
- package/skills/sia-visualize-live/scripts/graph-template.html +389 -0
- package/skills/sia-visualize-live/scripts/start-visualizer.sh +161 -0
- package/skills/sia-visualize-live/scripts/stop-visualizer.sh +55 -0
- package/skills/sia-visualize-live/scripts/visualizer-server.cjs +264 -0
- package/skills/sia-workspace/SKILL.md +57 -0
- package/src/agent/claude-md-template-flagging.md +219 -0
- package/src/agent/claude-md-template.md +213 -0
- package/src/agent/modules/sia-feature.md +100 -0
- package/src/agent/modules/sia-flagging.md +50 -0
- package/src/agent/modules/sia-orientation.md +92 -0
- package/src/agent/modules/sia-regression.md +115 -0
- package/src/agent/modules/sia-review.md +64 -0
- package/src/agent/modules/sia-tools.md +239 -0
- package/src/ast/extractors/c-include.ts +189 -0
- package/src/ast/extractors/csharp-project.ts +260 -0
- package/src/ast/extractors/prisma-schema.ts +44 -0
- package/src/ast/extractors/project-manifest.ts +111 -0
- package/src/ast/extractors/sql-schema.ts +67 -0
- package/src/ast/extractors/tier-a.ts +423 -0
- package/src/ast/extractors/tier-b.ts +289 -0
- package/src/ast/extractors/tier-dispatch.ts +247 -0
- package/src/ast/index-worker.ts +108 -0
- package/src/ast/indexer.ts +484 -0
- package/src/ast/languages.ts +408 -0
- package/src/ast/pagerank-builder.ts +125 -0
- package/src/ast/path-utils.ts +137 -0
- package/src/ast/tree-sitter/backends/native.ts +57 -0
- package/src/ast/tree-sitter/backends/wasm.ts +39 -0
- package/src/ast/tree-sitter/call-walker.ts +44 -0
- package/src/ast/tree-sitter/edit-computer.ts +55 -0
- package/src/ast/tree-sitter/query-runner.ts +46 -0
- package/src/ast/tree-sitter/service.ts +174 -0
- package/src/ast/tree-sitter/tree-cache.ts +39 -0
- package/src/ast/tree-sitter/types.ts +79 -0
- package/src/ast/watcher.ts +322 -0
- package/src/capture/chunker.ts +169 -0
- package/src/capture/consolidate.ts +127 -0
- package/src/capture/edge-inferrer.ts +161 -0
- package/src/capture/embedder.ts +166 -0
- package/src/capture/embedding-cache.ts +73 -0
- package/src/capture/flag-processor.ts +64 -0
- package/src/capture/hook.ts +67 -0
- package/src/capture/pipeline.ts +450 -0
- package/src/capture/prompts/consolidate.ts +25 -0
- package/src/capture/prompts/edge-infer.ts +29 -0
- package/src/capture/prompts/extract-flagged.ts +36 -0
- package/src/capture/prompts/extract.ts +42 -0
- package/src/capture/tokenizer.ts +147 -0
- package/src/capture/track-a-ast.ts +93 -0
- package/src/capture/track-b-llm.ts +149 -0
- package/src/capture/types.ts +64 -0
- package/src/cli/commands/community.ts +137 -0
- package/src/cli/commands/compare.ts +123 -0
- package/src/cli/commands/conflicts.ts +41 -0
- package/src/cli/commands/digest.ts +197 -0
- package/src/cli/commands/disable-flagging.ts +34 -0
- package/src/cli/commands/doctor.ts +240 -0
- package/src/cli/commands/download-model.ts +161 -0
- package/src/cli/commands/enable-flagging.ts +34 -0
- package/src/cli/commands/export-knowledge.ts +208 -0
- package/src/cli/commands/export.ts +85 -0
- package/src/cli/commands/freshness.ts +164 -0
- package/src/cli/commands/graph.ts +51 -0
- package/src/cli/commands/history.ts +139 -0
- package/src/cli/commands/import.ts +335 -0
- package/src/cli/commands/install.ts +156 -0
- package/src/cli/commands/lead-report.ts +241 -0
- package/src/cli/commands/learn.ts +321 -0
- package/src/cli/commands/pm-report.ts +413 -0
- package/src/cli/commands/prune.ts +75 -0
- package/src/cli/commands/qa-report.ts +278 -0
- package/src/cli/commands/reindex.ts +104 -0
- package/src/cli/commands/rollback.ts +70 -0
- package/src/cli/commands/search.ts +103 -0
- package/src/cli/commands/server.ts +91 -0
- package/src/cli/commands/share.ts +33 -0
- package/src/cli/commands/stats.ts +79 -0
- package/src/cli/commands/status.ts +176 -0
- package/src/cli/commands/sync.ts +96 -0
- package/src/cli/commands/team.ts +118 -0
- package/src/cli/commands/tour.ts +157 -0
- package/src/cli/commands/visualize-live.ts +162 -0
- package/src/cli/commands/workspace.ts +117 -0
- package/src/cli/index.ts +424 -0
- package/src/cli/learn-progress.ts +87 -0
- package/src/community/detection-bridge.ts +344 -0
- package/src/community/leiden.ts +462 -0
- package/src/community/raptor.ts +210 -0
- package/src/community/scheduler.ts +74 -0
- package/src/community/summarize.ts +115 -0
- package/src/decay/archiver.ts +73 -0
- package/src/decay/bridge-orphan-cleanup.ts +212 -0
- package/src/decay/consolidation-sweep.ts +112 -0
- package/src/decay/decay.ts +116 -0
- package/src/decay/deep-validator.ts +62 -0
- package/src/decay/episodic-promoter.ts +132 -0
- package/src/decay/maintenance-scheduler.ts +326 -0
- package/src/decay/scheduler.ts +6 -0
- package/src/decay/session-sweeper.ts +79 -0
- package/src/decay/types.ts +17 -0
- package/src/freshness/confidence-decay.ts +122 -0
- package/src/freshness/cuckoo-filter.ts +176 -0
- package/src/freshness/deep-validation.ts +345 -0
- package/src/freshness/dirty-tracker.ts +237 -0
- package/src/freshness/file-watcher-layer.ts +119 -0
- package/src/freshness/firewall.ts +64 -0
- package/src/freshness/git-reconcile-layer.ts +161 -0
- package/src/freshness/inverted-index.ts +158 -0
- package/src/freshness/stale-read-layer.ts +222 -0
- package/src/graph/audit.ts +69 -0
- package/src/graph/bridge-db.ts +141 -0
- package/src/graph/communities.ts +195 -0
- package/src/graph/db-interface.ts +259 -0
- package/src/graph/edges.ts +163 -0
- package/src/graph/entities.ts +327 -0
- package/src/graph/episodic-db.ts +113 -0
- package/src/graph/flags.ts +31 -0
- package/src/graph/meta-db.ts +200 -0
- package/src/graph/semantic-db.ts +101 -0
- package/src/graph/session-resume.ts +56 -0
- package/src/graph/snapshots.ts +342 -0
- package/src/graph/staging.ts +151 -0
- package/src/graph/types.ts +128 -0
- package/src/hooks/adapters/claude-code.ts +21 -0
- package/src/hooks/adapters/cline.ts +43 -0
- package/src/hooks/adapters/cursor.ts +65 -0
- package/src/hooks/adapters/generic.ts +12 -0
- package/src/hooks/agent-detect.ts +34 -0
- package/src/hooks/claude-md-directives.ts +32 -0
- package/src/hooks/event-router.ts +182 -0
- package/src/hooks/extractors/pattern-detector.ts +111 -0
- package/src/hooks/handlers/post-compact.ts +30 -0
- package/src/hooks/handlers/post-tool-use.ts +403 -0
- package/src/hooks/handlers/pre-compact.ts +100 -0
- package/src/hooks/handlers/session-end.ts +47 -0
- package/src/hooks/handlers/session-start.ts +154 -0
- package/src/hooks/handlers/stop.ts +128 -0
- package/src/hooks/handlers/user-prompt-submit.ts +68 -0
- package/src/hooks/plugin-branch-switch.ts +68 -0
- package/src/hooks/plugin-common.ts +47 -0
- package/src/hooks/plugin-post-compact.ts +28 -0
- package/src/hooks/plugin-post-tool-use.ts +38 -0
- package/src/hooks/plugin-pre-compact.ts +37 -0
- package/src/hooks/plugin-session-end.ts +37 -0
- package/src/hooks/plugin-session-start.ts +75 -0
- package/src/hooks/plugin-stop.ts +61 -0
- package/src/hooks/plugin-user-prompt-submit.ts +47 -0
- package/src/hooks/types.ts +43 -0
- package/src/knowledge/discovery.ts +238 -0
- package/src/knowledge/external-refs.ts +98 -0
- package/src/knowledge/freshness.ts +221 -0
- package/src/knowledge/ingest.ts +330 -0
- package/src/knowledge/markdown-export.ts +229 -0
- package/src/knowledge/markdown-import.ts +359 -0
- package/src/knowledge/patterns.ts +74 -0
- package/src/knowledge/templates.ts +307 -0
- package/src/llm/ai-sdk-adapter.ts +46 -0
- package/src/llm/config.ts +88 -0
- package/src/llm/cost-tracker.ts +110 -0
- package/src/llm/prompts/extraction.ts +55 -0
- package/src/llm/prompts/summarization.ts +36 -0
- package/src/llm/prompts/validation.ts +37 -0
- package/src/llm/provider-registry.ts +68 -0
- package/src/llm/reliability.ts +179 -0
- package/src/llm/schemas.ts +52 -0
- package/src/mcp/freshness-annotator.ts +69 -0
- package/src/mcp/server.ts +949 -0
- package/src/mcp/tools/sia-ast-query.ts +225 -0
- package/src/mcp/tools/sia-at-time.ts +151 -0
- package/src/mcp/tools/sia-backlinks.ts +87 -0
- package/src/mcp/tools/sia-batch-execute.ts +169 -0
- package/src/mcp/tools/sia-by-file.ts +89 -0
- package/src/mcp/tools/sia-community.ts +113 -0
- package/src/mcp/tools/sia-doctor.ts +73 -0
- package/src/mcp/tools/sia-execute-file.ts +122 -0
- package/src/mcp/tools/sia-execute.ts +104 -0
- package/src/mcp/tools/sia-expand.ts +158 -0
- package/src/mcp/tools/sia-fetch-and-index.ts +241 -0
- package/src/mcp/tools/sia-flag.ts +65 -0
- package/src/mcp/tools/sia-index.ts +111 -0
- package/src/mcp/tools/sia-note.ts +134 -0
- package/src/mcp/tools/sia-search.ts +105 -0
- package/src/mcp/tools/sia-stats.ts +63 -0
- package/src/mcp/tools/sia-sync-status.ts +44 -0
- package/src/mcp/tools/sia-upgrade.ts +247 -0
- package/src/mcp/truncate.ts +231 -0
- package/src/native/bridge.ts +167 -0
- package/src/native/fallback-ast-diff.ts +144 -0
- package/src/native/fallback-graph.ts +325 -0
- package/src/ontology/constraints.ts +56 -0
- package/src/ontology/errors.ts +8 -0
- package/src/ontology/middleware.ts +266 -0
- package/src/retrieval/bm25-search.ts +151 -0
- package/src/retrieval/context-assembly.ts +76 -0
- package/src/retrieval/graph-traversal.ts +168 -0
- package/src/retrieval/pagerank.ts +40 -0
- package/src/retrieval/query-classifier.ts +106 -0
- package/src/retrieval/reranker.ts +156 -0
- package/src/retrieval/search.ts +236 -0
- package/src/retrieval/throttle.ts +102 -0
- package/src/retrieval/vector-search.ts +203 -0
- package/src/retrieval/workspace-search.ts +130 -0
- package/src/sandbox/context-mode.ts +285 -0
- package/src/sandbox/credential-pass.ts +55 -0
- package/src/sandbox/executor.ts +235 -0
- package/src/security/pattern-detector.ts +127 -0
- package/src/security/rule-of-two.ts +50 -0
- package/src/security/sanitize.ts +46 -0
- package/src/security/semantic-consistency.ts +93 -0
- package/src/security/staging-promoter.ts +154 -0
- package/src/shared/config.ts +302 -0
- package/src/shared/diagnostics.ts +210 -0
- package/src/shared/errors.ts +48 -0
- package/src/shared/git-utils.ts +143 -0
- package/src/shared/llm-client.ts +120 -0
- package/src/shared/logger.ts +99 -0
- package/src/shared/types.ts +79 -0
- package/src/sync/client.ts +43 -0
- package/src/sync/conflict.ts +106 -0
- package/src/sync/dedup.ts +183 -0
- package/src/sync/hlc.ts +117 -0
- package/src/sync/keychain.ts +144 -0
- package/src/sync/pull.ts +232 -0
- package/src/sync/push.ts +131 -0
- package/src/types/chokidar.d.ts +23 -0
- package/src/visualization/graph-renderer.ts +312 -0
- package/src/visualization/subgraph-extract.ts +208 -0
- package/src/visualization/views/community-clusters.ts +246 -0
- package/src/visualization/views/dependency-map.ts +189 -0
- package/src/visualization/views/graph-explorer.ts +364 -0
- package/src/visualization/views/timeline.ts +247 -0
- package/src/workspace/api-contracts.ts +226 -0
- package/src/workspace/cross-repo.ts +61 -0
- package/src/workspace/detector.ts +190 -0
- package/src/workspace/manifest.ts +141 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
// Module: inverted-index — CRUD for the source_deps inverted dependency index
|
|
2
|
+
//
|
|
3
|
+
// Maps source files to the graph entities derived from them. When a file
|
|
4
|
+
// changes, getDependentsForFile returns the exact set of entities that may
|
|
5
|
+
// be stale and require revalidation.
|
|
6
|
+
|
|
7
|
+
import type { SiaDb } from "@/graph/db-interface";
|
|
8
|
+
|
|
9
|
+
/** A single source-file-to-entity dependency mapping. */
|
|
10
|
+
export interface SourceDep {
|
|
11
|
+
source_path: string;
|
|
12
|
+
node_id: string;
|
|
13
|
+
dep_type: "defines" | "extracted_from" | "pertains_to" | "references";
|
|
14
|
+
source_mtime: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Add a source -> node dependency mapping.
|
|
19
|
+
* Uses INSERT OR REPLACE for idempotency — re-inserting the same
|
|
20
|
+
* (source_path, node_id) pair updates dep_type and source_mtime.
|
|
21
|
+
*/
|
|
22
|
+
export async function addDependency(db: SiaDb, dep: SourceDep): Promise<void> {
|
|
23
|
+
await db.execute(
|
|
24
|
+
`INSERT OR REPLACE INTO source_deps (source_path, node_id, dep_type, source_mtime)
|
|
25
|
+
VALUES (?, ?, ?, ?)`,
|
|
26
|
+
[dep.source_path, dep.node_id, dep.dep_type, dep.source_mtime],
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Remove a specific source -> node dependency. */
|
|
31
|
+
export async function removeDependency(
|
|
32
|
+
db: SiaDb,
|
|
33
|
+
sourcePath: string,
|
|
34
|
+
nodeId: string,
|
|
35
|
+
): Promise<void> {
|
|
36
|
+
await db.execute("DELETE FROM source_deps WHERE source_path = ? AND node_id = ?", [
|
|
37
|
+
sourcePath,
|
|
38
|
+
nodeId,
|
|
39
|
+
]);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Remove all dependencies for a node (used when a node is invalidated). */
|
|
43
|
+
export async function removeDependenciesForNode(db: SiaDb, nodeId: string): Promise<void> {
|
|
44
|
+
await db.execute("DELETE FROM source_deps WHERE node_id = ?", [nodeId]);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Get all nodes derived from a source file.
|
|
49
|
+
* This is the core invalidation query — when file X changes, this returns
|
|
50
|
+
* every entity that may need revalidation.
|
|
51
|
+
*/
|
|
52
|
+
export async function getDependentsForFile(db: SiaDb, sourcePath: string): Promise<SourceDep[]> {
|
|
53
|
+
const { rows } = await db.execute(
|
|
54
|
+
"SELECT source_path, node_id, dep_type, source_mtime FROM source_deps WHERE source_path = ?",
|
|
55
|
+
[sourcePath],
|
|
56
|
+
);
|
|
57
|
+
return rows as unknown as SourceDep[];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Get all source files that an entity depends on. */
|
|
61
|
+
export async function getDependenciesForNode(db: SiaDb, nodeId: string): Promise<SourceDep[]> {
|
|
62
|
+
const { rows } = await db.execute(
|
|
63
|
+
"SELECT source_path, node_id, dep_type, source_mtime FROM source_deps WHERE node_id = ?",
|
|
64
|
+
[nodeId],
|
|
65
|
+
);
|
|
66
|
+
return rows as unknown as SourceDep[];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Get all distinct source paths (used for Cuckoo filter rebuild). */
|
|
70
|
+
export async function getAllSourcePaths(db: SiaDb): Promise<string[]> {
|
|
71
|
+
const { rows } = await db.execute("SELECT DISTINCT source_path FROM source_deps");
|
|
72
|
+
return rows.map((r) => r.source_path as string);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Rebuild the entire inverted index from existing graph data.
|
|
77
|
+
*
|
|
78
|
+
* - For each entity with a non-empty `file_paths` JSON array, creates
|
|
79
|
+
* 'defines' dependencies from each path to that entity.
|
|
80
|
+
* - For each active `pertains_to` edge whose target entity has file_paths,
|
|
81
|
+
* creates 'pertains_to' dependencies from those target paths to the
|
|
82
|
+
* source entity.
|
|
83
|
+
*
|
|
84
|
+
* Returns the total count of dependencies created.
|
|
85
|
+
*/
|
|
86
|
+
export async function rebuildFromGraph(db: SiaDb): Promise<number> {
|
|
87
|
+
// Clear existing deps
|
|
88
|
+
await db.execute("DELETE FROM source_deps", []);
|
|
89
|
+
|
|
90
|
+
let count = 0;
|
|
91
|
+
const now = Date.now();
|
|
92
|
+
|
|
93
|
+
// 1. Entities with file_paths -> 'defines' deps
|
|
94
|
+
const { rows: entities } = await db.execute(
|
|
95
|
+
`SELECT id, file_paths FROM graph_nodes
|
|
96
|
+
WHERE archived_at IS NULL AND t_valid_until IS NULL
|
|
97
|
+
AND file_paths IS NOT NULL AND file_paths != '[]'`,
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
for (const entity of entities) {
|
|
101
|
+
const id = entity.id as string;
|
|
102
|
+
const filePathsRaw = entity.file_paths as string;
|
|
103
|
+
let paths: string[];
|
|
104
|
+
try {
|
|
105
|
+
paths = JSON.parse(filePathsRaw);
|
|
106
|
+
} catch {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (!Array.isArray(paths)) continue;
|
|
110
|
+
|
|
111
|
+
for (const p of paths) {
|
|
112
|
+
if (typeof p !== "string" || p.length === 0) continue;
|
|
113
|
+
await addDependency(db, {
|
|
114
|
+
source_path: p,
|
|
115
|
+
node_id: id,
|
|
116
|
+
dep_type: "defines",
|
|
117
|
+
source_mtime: now,
|
|
118
|
+
});
|
|
119
|
+
count++;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// 2. pertains_to edges: source entity depends on target's file_paths
|
|
124
|
+
const { rows: edges } = await db.execute(
|
|
125
|
+
`SELECT e.from_id, t.file_paths
|
|
126
|
+
FROM graph_edges e
|
|
127
|
+
JOIN graph_nodes t ON t.id = e.to_id
|
|
128
|
+
WHERE e.type = 'pertains_to'
|
|
129
|
+
AND e.t_valid_until IS NULL
|
|
130
|
+
AND t.archived_at IS NULL AND t.t_valid_until IS NULL
|
|
131
|
+
AND t.file_paths IS NOT NULL AND t.file_paths != '[]'`,
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
for (const edge of edges) {
|
|
135
|
+
const fromId = edge.from_id as string;
|
|
136
|
+
const filePathsRaw = edge.file_paths as string;
|
|
137
|
+
let paths: string[];
|
|
138
|
+
try {
|
|
139
|
+
paths = JSON.parse(filePathsRaw);
|
|
140
|
+
} catch {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (!Array.isArray(paths)) continue;
|
|
144
|
+
|
|
145
|
+
for (const p of paths) {
|
|
146
|
+
if (typeof p !== "string" || p.length === 0) continue;
|
|
147
|
+
await addDependency(db, {
|
|
148
|
+
source_path: p,
|
|
149
|
+
node_id: fromId,
|
|
150
|
+
dep_type: "pertains_to",
|
|
151
|
+
source_mtime: now,
|
|
152
|
+
});
|
|
153
|
+
count++;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return count;
|
|
158
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
// Module: stale-read-layer — Layer 3 per-query freshness validation
|
|
2
|
+
//
|
|
3
|
+
// Provides stale-while-revalidate semantics for node reads.
|
|
4
|
+
// When a node is accessed during retrieval this layer determines whether
|
|
5
|
+
// it is Fresh, Stale, or Rotten by comparing source file mtimes against
|
|
6
|
+
// the node's t_created timestamp.
|
|
7
|
+
//
|
|
8
|
+
// Fresh: source unchanged since extraction → serve immediately (< 0.05ms)
|
|
9
|
+
// Stale: source modified within window → serve + async re-validate
|
|
10
|
+
// Rotten: source modified beyond window → block until re-validated
|
|
11
|
+
//
|
|
12
|
+
// For nodes with multiple source files, only the most-recently-modified
|
|
13
|
+
// source is stat()-checked — if it has not changed, none have (optimistic
|
|
14
|
+
// fast path).
|
|
15
|
+
|
|
16
|
+
import { createHash } from "node:crypto";
|
|
17
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
18
|
+
import { join } from "node:path";
|
|
19
|
+
import type { SiaDb } from "@/graph/db-interface";
|
|
20
|
+
import type { DirtyTracker } from "./dirty-tracker";
|
|
21
|
+
import { getDependenciesForNode } from "./inverted-index";
|
|
22
|
+
|
|
23
|
+
export type FreshnessState = "fresh" | "stale" | "rotten";
|
|
24
|
+
|
|
25
|
+
export interface FreshnessCheck {
|
|
26
|
+
state: FreshnessState;
|
|
27
|
+
sourcePath?: string; // the source file checked
|
|
28
|
+
sourceMtime?: number; // source file mtime (ms)
|
|
29
|
+
extractionTime?: number; // when the node was extracted (t_created ms)
|
|
30
|
+
divergenceSeconds?: number; // (mtime - t_created) / 1000
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface StalenessConfig {
|
|
34
|
+
activeEditWindowMs: number; // default 30_000 (30s)
|
|
35
|
+
sessionCommitWindowMs: number; // default 300_000 (5min)
|
|
36
|
+
defaultWindowMs: number; // default Infinity (event-driven via Layers 1-2)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const DEFAULT_CONFIG: StalenessConfig = {
|
|
40
|
+
activeEditWindowMs: 30_000,
|
|
41
|
+
sessionCommitWindowMs: 300_000,
|
|
42
|
+
defaultWindowMs: Number.POSITIVE_INFINITY,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
// checkFreshness
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Check the freshness of a node by comparing source file mtimes.
|
|
51
|
+
*
|
|
52
|
+
* Algorithm:
|
|
53
|
+
* 1. If tracker.checkNode(nodeId) is 'clean' → return Fresh (no stat() needed)
|
|
54
|
+
* 2. Get source files via getDependenciesForNode()
|
|
55
|
+
* 3. If no source files → return Fresh (no dependency to check)
|
|
56
|
+
* 4. stat() the most recently modified source file
|
|
57
|
+
* 5. Compare mtime against node's t_created timestamp
|
|
58
|
+
* 6. If mtime <= t_created → Fresh (source unchanged)
|
|
59
|
+
* 7. If mtime > t_created and within staleness window → Stale
|
|
60
|
+
* 8. If mtime > t_created and beyond staleness window → Rotten
|
|
61
|
+
*/
|
|
62
|
+
export async function checkFreshness(
|
|
63
|
+
db: SiaDb,
|
|
64
|
+
nodeId: string,
|
|
65
|
+
tracker: DirtyTracker,
|
|
66
|
+
repoRoot: string,
|
|
67
|
+
config?: Partial<StalenessConfig>,
|
|
68
|
+
): Promise<FreshnessCheck> {
|
|
69
|
+
const cfg: StalenessConfig = { ...DEFAULT_CONFIG, ...config };
|
|
70
|
+
|
|
71
|
+
// Step 1: fast path — if tracker says clean, no stat() needed
|
|
72
|
+
const dirtyState = tracker.checkNode(nodeId);
|
|
73
|
+
if (dirtyState === "clean") {
|
|
74
|
+
return { state: "fresh" };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Step 2: get source dependencies for this node
|
|
78
|
+
const deps = await getDependenciesForNode(db, nodeId);
|
|
79
|
+
|
|
80
|
+
// Step 3: no dependencies → treat as fresh (nothing to check)
|
|
81
|
+
if (deps.length === 0) {
|
|
82
|
+
return { state: "fresh" };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Step 4: select the most recently modified source file recorded in source_deps.
|
|
86
|
+
// source_mtime is the recorded mtime at extraction time; the dep with the
|
|
87
|
+
// highest recorded mtime is most likely to have diverged.
|
|
88
|
+
const primaryDep = deps.reduce((best, d) => (d.source_mtime > best.source_mtime ? d : best));
|
|
89
|
+
|
|
90
|
+
const sourcePath = primaryDep.source_path;
|
|
91
|
+
|
|
92
|
+
// Step 4a: resolve path — if not absolute, join with repoRoot
|
|
93
|
+
const resolvedPath = sourcePath.startsWith("/") ? sourcePath : join(repoRoot, sourcePath);
|
|
94
|
+
|
|
95
|
+
// Handle missing file (deleted) → Rotten
|
|
96
|
+
if (!existsSync(resolvedPath)) {
|
|
97
|
+
return { state: "rotten", sourcePath };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
let sourceMtime: number;
|
|
101
|
+
try {
|
|
102
|
+
sourceMtime = statSync(resolvedPath).mtimeMs;
|
|
103
|
+
} catch {
|
|
104
|
+
// stat error (permission denied, race condition, etc.) → Rotten
|
|
105
|
+
return { state: "rotten", sourcePath };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Step 5: get node's t_created from the database
|
|
109
|
+
const { rows } = await db.execute("SELECT t_created FROM graph_nodes WHERE id = ?", [nodeId]);
|
|
110
|
+
if (rows.length === 0) {
|
|
111
|
+
// Node not found — treat as rotten
|
|
112
|
+
return { state: "rotten", sourcePath, sourceMtime };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const extractionTime = rows[0].t_created as number;
|
|
116
|
+
|
|
117
|
+
// Step 6: source unchanged → Fresh
|
|
118
|
+
if (sourceMtime <= extractionTime) {
|
|
119
|
+
return { state: "fresh", sourcePath, sourceMtime, extractionTime };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Source was modified after extraction — compute divergence
|
|
123
|
+
const divergenceMs = sourceMtime - extractionTime;
|
|
124
|
+
const divergenceSeconds = divergenceMs / 1000;
|
|
125
|
+
|
|
126
|
+
// Step 7: within staleness window → Stale
|
|
127
|
+
if (divergenceMs <= cfg.activeEditWindowMs || divergenceMs <= cfg.sessionCommitWindowMs) {
|
|
128
|
+
return { state: "stale", sourcePath, sourceMtime, extractionTime, divergenceSeconds };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Step 8: beyond staleness window → Rotten
|
|
132
|
+
return { state: "rotten", sourcePath, sourceMtime, extractionTime, divergenceSeconds };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ---------------------------------------------------------------------------
|
|
136
|
+
// readRepair
|
|
137
|
+
// ---------------------------------------------------------------------------
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Perform read-repair: re-read source and compare content hash against the
|
|
141
|
+
* stored node content.
|
|
142
|
+
*
|
|
143
|
+
* Algorithm:
|
|
144
|
+
* 1. Get source files for the node
|
|
145
|
+
* 2. Re-read the primary source file content
|
|
146
|
+
* 3. Hash the content and compare against stored content hash
|
|
147
|
+
* 4. If content unchanged → tracker.markClean(nodeId) (early cutoff), return false
|
|
148
|
+
* 5. If changed → update node, tracker.markCleanAndPropagate(), return true
|
|
149
|
+
*
|
|
150
|
+
* Returns true if the node content changed (not an early cutoff).
|
|
151
|
+
*/
|
|
152
|
+
export async function readRepair(
|
|
153
|
+
db: SiaDb,
|
|
154
|
+
nodeId: string,
|
|
155
|
+
tracker: DirtyTracker,
|
|
156
|
+
repoRoot: string,
|
|
157
|
+
): Promise<boolean> {
|
|
158
|
+
// Step 1: get source files for the node
|
|
159
|
+
const deps = await getDependenciesForNode(db, nodeId);
|
|
160
|
+
|
|
161
|
+
if (deps.length === 0) {
|
|
162
|
+
// No source to compare — mark clean (nothing to repair)
|
|
163
|
+
tracker.markClean(nodeId);
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Select the primary source (highest recorded mtime — most likely changed)
|
|
168
|
+
const primaryDep = deps.reduce((best, d) => (d.source_mtime > best.source_mtime ? d : best));
|
|
169
|
+
|
|
170
|
+
const sourcePath = primaryDep.source_path;
|
|
171
|
+
const resolvedPath = sourcePath.startsWith("/") ? sourcePath : join(repoRoot, sourcePath);
|
|
172
|
+
|
|
173
|
+
// Step 2: re-read the source file
|
|
174
|
+
let sourceContent: string;
|
|
175
|
+
try {
|
|
176
|
+
sourceContent = readFileSync(resolvedPath, "utf8");
|
|
177
|
+
} catch {
|
|
178
|
+
// File unreadable (deleted, permission error) — mark clean to avoid
|
|
179
|
+
// repeated repair attempts; caller should handle rotten state separately
|
|
180
|
+
tracker.markClean(nodeId);
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Step 3: hash the current source content
|
|
185
|
+
const sourceHash = sha256(sourceContent);
|
|
186
|
+
|
|
187
|
+
// Fetch the stored node content and hash it
|
|
188
|
+
const { rows } = await db.execute("SELECT content FROM graph_nodes WHERE id = ?", [nodeId]);
|
|
189
|
+
|
|
190
|
+
if (rows.length === 0) {
|
|
191
|
+
tracker.markClean(nodeId);
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const storedContent = rows[0].content as string;
|
|
196
|
+
const storedHash = sha256(storedContent);
|
|
197
|
+
|
|
198
|
+
// Step 4: hashes match — content unchanged (early cutoff)
|
|
199
|
+
if (sourceHash === storedHash) {
|
|
200
|
+
tracker.markClean(nodeId);
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Step 5: content changed — update the node and propagate
|
|
205
|
+
const now = Date.now();
|
|
206
|
+
await db.execute("UPDATE graph_nodes SET content = ?, t_created = ? WHERE id = ?", [
|
|
207
|
+
sourceContent,
|
|
208
|
+
now,
|
|
209
|
+
nodeId,
|
|
210
|
+
]);
|
|
211
|
+
|
|
212
|
+
await tracker.markCleanAndPropagate(db, nodeId);
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ---------------------------------------------------------------------------
|
|
217
|
+
// Internal helpers
|
|
218
|
+
// ---------------------------------------------------------------------------
|
|
219
|
+
|
|
220
|
+
function sha256(data: string): string {
|
|
221
|
+
return createHash("sha256").update(data).digest("hex");
|
|
222
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// Module: audit — Append-only audit log write layer
|
|
2
|
+
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
import type { SiaDb } from "@/graph/db-interface";
|
|
5
|
+
|
|
6
|
+
/** All supported audit operations (ARCHI v4.1 schema). */
|
|
7
|
+
export type AuditOperation =
|
|
8
|
+
| "ADD"
|
|
9
|
+
| "UPDATE"
|
|
10
|
+
| "INVALIDATE"
|
|
11
|
+
| "NOOP"
|
|
12
|
+
| "STAGE"
|
|
13
|
+
| "PROMOTE"
|
|
14
|
+
| "QUARANTINE"
|
|
15
|
+
| "SYNC_RECV"
|
|
16
|
+
| "SYNC_SEND"
|
|
17
|
+
| "ARCHIVE"
|
|
18
|
+
| "VSS_REFRESH"
|
|
19
|
+
| "PAGERANK_UPDATE";
|
|
20
|
+
|
|
21
|
+
/** Optional detail fields matching audit_log columns. */
|
|
22
|
+
export interface AuditDetails {
|
|
23
|
+
entity_id?: string;
|
|
24
|
+
edge_id?: string;
|
|
25
|
+
source_episode?: string;
|
|
26
|
+
trust_tier?: number;
|
|
27
|
+
extraction_method?: string;
|
|
28
|
+
source_hash?: string;
|
|
29
|
+
developer_id?: string;
|
|
30
|
+
snapshot_id?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Write a single append-only audit log entry.
|
|
35
|
+
*
|
|
36
|
+
* NEVER throws — any DB error is caught and logged to console.error.
|
|
37
|
+
* There is intentionally no update or delete export from this module.
|
|
38
|
+
*/
|
|
39
|
+
export async function writeAuditEntry(
|
|
40
|
+
db: SiaDb,
|
|
41
|
+
op: AuditOperation,
|
|
42
|
+
details: AuditDetails = {},
|
|
43
|
+
): Promise<void> {
|
|
44
|
+
try {
|
|
45
|
+
await db.execute(
|
|
46
|
+
`INSERT INTO audit_log (
|
|
47
|
+
id, ts, operation,
|
|
48
|
+
entity_id, edge_id, source_episode,
|
|
49
|
+
trust_tier, extraction_method, source_hash,
|
|
50
|
+
developer_id, snapshot_id
|
|
51
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
52
|
+
[
|
|
53
|
+
randomUUID(),
|
|
54
|
+
Date.now(),
|
|
55
|
+
op,
|
|
56
|
+
details.entity_id ?? null,
|
|
57
|
+
details.edge_id ?? null,
|
|
58
|
+
details.source_episode ?? null,
|
|
59
|
+
details.trust_tier ?? null,
|
|
60
|
+
details.extraction_method ?? null,
|
|
61
|
+
details.source_hash ?? null,
|
|
62
|
+
details.developer_id ?? null,
|
|
63
|
+
details.snapshot_id ?? null,
|
|
64
|
+
],
|
|
65
|
+
);
|
|
66
|
+
} catch (err) {
|
|
67
|
+
console.error("writeAuditEntry failed:", err);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// Module: bridge-db — Bridge database opener and cross-repo edge CRUD
|
|
2
|
+
|
|
3
|
+
import { randomUUID } from "node:crypto";
|
|
4
|
+
import { join, resolve } from "node:path";
|
|
5
|
+
import type { BunSqliteDb, SiaDb } from "@/graph/db-interface";
|
|
6
|
+
import { runMigrations } from "@/graph/semantic-db";
|
|
7
|
+
import { SIA_HOME } from "@/shared/config";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Open (or create) the global bridge database.
|
|
11
|
+
* Resolves to `{siaHome}/bridge.db` (not under repos/) and applies
|
|
12
|
+
* migrations from the `migrations/bridge` directory.
|
|
13
|
+
*/
|
|
14
|
+
export function openBridgeDb(siaHome?: string): BunSqliteDb {
|
|
15
|
+
const home = siaHome ?? SIA_HOME;
|
|
16
|
+
const dbPath = join(home, "bridge.db");
|
|
17
|
+
const migrationsDir = resolve(import.meta.dirname, "../../migrations/bridge");
|
|
18
|
+
return runMigrations(dbPath, migrationsDir);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// Cross-repo edge types
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
/** Shape accepted by insertCrossRepoEdge. Caller supplies endpoints and classification. */
|
|
26
|
+
export interface NewCrossRepoEdge {
|
|
27
|
+
source_repo_id: string;
|
|
28
|
+
source_entity_id: string;
|
|
29
|
+
target_repo_id: string;
|
|
30
|
+
target_entity_id: string;
|
|
31
|
+
type: string;
|
|
32
|
+
weight?: number;
|
|
33
|
+
confidence?: number;
|
|
34
|
+
trust_tier?: number;
|
|
35
|
+
properties?: string | null;
|
|
36
|
+
t_valid_from?: number | null;
|
|
37
|
+
created_by?: string | null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Row shape returned by cross-repo edge queries. */
|
|
41
|
+
export interface CrossRepoEdgeRow {
|
|
42
|
+
id: string;
|
|
43
|
+
source_repo_id: string;
|
|
44
|
+
source_entity_id: string;
|
|
45
|
+
target_repo_id: string;
|
|
46
|
+
target_entity_id: string;
|
|
47
|
+
type: string;
|
|
48
|
+
weight: number;
|
|
49
|
+
confidence: number;
|
|
50
|
+
trust_tier: number;
|
|
51
|
+
properties: string | null;
|
|
52
|
+
t_created: number;
|
|
53
|
+
t_expired: number | null;
|
|
54
|
+
t_valid_from: number | null;
|
|
55
|
+
t_valid_until: number | null;
|
|
56
|
+
hlc_created: number | null;
|
|
57
|
+
hlc_modified: number | null;
|
|
58
|
+
created_by: string | null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
// Cross-repo edge CRUD
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Insert a new cross-repo edge into bridge.db.
|
|
67
|
+
*
|
|
68
|
+
* Generates a UUID for `id`, sets `t_created = Date.now()`.
|
|
69
|
+
* Returns the generated id.
|
|
70
|
+
*/
|
|
71
|
+
export async function insertCrossRepoEdge(db: SiaDb, edge: NewCrossRepoEdge): Promise<string> {
|
|
72
|
+
const id = randomUUID();
|
|
73
|
+
const now = Date.now();
|
|
74
|
+
|
|
75
|
+
await db.execute(
|
|
76
|
+
`INSERT INTO cross_repo_edges (
|
|
77
|
+
id, source_repo_id, source_entity_id,
|
|
78
|
+
target_repo_id, target_entity_id,
|
|
79
|
+
type, weight, confidence, trust_tier, properties,
|
|
80
|
+
t_created, t_expired, t_valid_from, t_valid_until,
|
|
81
|
+
created_by
|
|
82
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, NULL, ?)`,
|
|
83
|
+
[
|
|
84
|
+
id,
|
|
85
|
+
edge.source_repo_id,
|
|
86
|
+
edge.source_entity_id,
|
|
87
|
+
edge.target_repo_id,
|
|
88
|
+
edge.target_entity_id,
|
|
89
|
+
edge.type,
|
|
90
|
+
edge.weight ?? 1.0,
|
|
91
|
+
edge.confidence ?? 0.9,
|
|
92
|
+
edge.trust_tier ?? 2,
|
|
93
|
+
edge.properties ?? null,
|
|
94
|
+
now,
|
|
95
|
+
edge.t_valid_from ?? null,
|
|
96
|
+
edge.created_by ?? null,
|
|
97
|
+
],
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
return id;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Invalidate a cross-repo edge (soft-delete).
|
|
105
|
+
*
|
|
106
|
+
* Sets BOTH `t_valid_until` AND `t_expired` to the given timestamp (default: now).
|
|
107
|
+
* Never hard-deletes.
|
|
108
|
+
*/
|
|
109
|
+
export async function invalidateCrossRepoEdge(
|
|
110
|
+
db: SiaDb,
|
|
111
|
+
id: string,
|
|
112
|
+
tValidUntil?: number,
|
|
113
|
+
): Promise<void> {
|
|
114
|
+
const ts = tValidUntil ?? Date.now();
|
|
115
|
+
|
|
116
|
+
await db.execute("UPDATE cross_repo_edges SET t_valid_until = ?, t_expired = ? WHERE id = ?", [
|
|
117
|
+
ts,
|
|
118
|
+
ts,
|
|
119
|
+
id,
|
|
120
|
+
]);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Get all currently active cross-repo edges for an entity (as source or target).
|
|
125
|
+
*
|
|
126
|
+
* Active = `t_valid_until IS NULL`.
|
|
127
|
+
*/
|
|
128
|
+
export async function getActiveCrossRepoEdgesFor(
|
|
129
|
+
db: SiaDb,
|
|
130
|
+
repoId: string,
|
|
131
|
+
entityId: string,
|
|
132
|
+
): Promise<CrossRepoEdgeRow[]> {
|
|
133
|
+
const result = await db.execute(
|
|
134
|
+
`SELECT * FROM cross_repo_edges
|
|
135
|
+
WHERE ((source_repo_id = ? AND source_entity_id = ?)
|
|
136
|
+
OR (target_repo_id = ? AND target_entity_id = ?))
|
|
137
|
+
AND t_valid_until IS NULL`,
|
|
138
|
+
[repoId, entityId, repoId, entityId],
|
|
139
|
+
);
|
|
140
|
+
return result.rows as unknown as CrossRepoEdgeRow[];
|
|
141
|
+
}
|