Generalized Views v1: v0.9-native reusable projections and legacy field repair #114
Labels
No labels
bug
documentation
duplicate
enhancement
good first issue
help wanted
invalid
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
nsaspy/starintel-server#114
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Parent: #61
Depends on API foundation: #58
Cross-repo evidence: lost-rob0t/starintel-gpt-auto-dig#2018
ADARD decision
Replace the legacy/accidental view surface with a versioned v0.9-native view registry derived from actual Auto-Dig corpus/query behavior. Do not add topic-specific views. Build a compact set of generic projections that answer the queries Auto-Dig, Quasar, dashboards, recursive workers, and public stats actually perform.
This issue is the implementation slice produced by the ADARD research/design loop. It must be TDD-first and exact-head verified.
A — Analyze / corpus evidence
Real corpus scale and pulse problem
Auto-Dig issue #2018 records a 30-day corpus snapshot with 1,254,577 documents across only 13 active days. Bulk materializations create huge spikes. The current dashboard groups only
date_added, conflating first-seen/materialization/research-event semantics.The Auto-Dig corpus dashboard currently recomputes over the complete corpus in Python:
These are direct candidates for indexed server projections.
Real v0.9 record shapes inspected
Representative current canonical records show:
_id,dataset,dtype,date_added,date_updated,status,verification,handling,assessment,sources,related_ids,temporal,provenance,workflow,data;data.subject,data.predicate,data.object, sometimesdata.confidence;workflow.research_status,workflow.recursion_depth,workflow.priority,workflow.run_id, plusdata.status,data.depth,data.priority,data.score,data.seed_ids;data;data.record_count,data.counts_by_dtype, generation/run metadata and coverage status;data.name/title, while older split-name fields are not guaranteed.Existing view defects against real data
source/views/data.jsoncurrently contains several legacy-shape projections that do not correctly index canonical v0.9 Auto-Dig records:relations_by_predicatereadsdoc.predicateinstead ofdoc.data.predicate;relations_out_degreereadsdoc.sourceinstead ofdoc.data.subject;relations_in_degreereadsdoc.targetinstead ofdoc.data.object;relations_self_loopshas the same legacy endpoint assumption;targets_by_actorassumesdtype === 'target'plus top-levelactor, which does not represent Auto-Diginvestigation-targetworkflow records;date_added_stats_by_datasetreads onlydateAdded;bad_docs_missing_metatreats snake-casedate_added/date_updatedas missing;persons/by_nameassumeslname/fname, while current migrated v0.9 people can carry onlydata.name;include_docs, increasing index size.This is correctness debt before it is optimization work.
D — Design
Create versioned design documents / manifest IDs. Exact naming may follow the registry machinery in #61, but semantic namespaces are fixed below.
1.
core-v1count_by_dtypeMap:
[dtype] -> 1; reduce_countor_sum.count_by_datasetMap:
[dataset] -> 1.count_by_dataset_dtypeMap:
[dataset, dtype] -> 1.Supports corpus composition globally or per dataset with
group_level.documents_by_datasetMap-only
[dataset, _id] -> null.Never emit the full document; use
include_docs=truewhen required.review_stateNormalize the same reviewed/unreviewed semantics Auto-Dig currently computes from:
verification.status;workflow.review_status/ workflow status;status.Emit
[dataset, normalized_review_state, dtype] -> 1.The normalization rule must be a shared/generated contract, not independently drifting Python and JavaScript token lists.
verification_statusEmit
[dataset, verification.status || 'unspecified', dtype] -> 1.Do not collapse detailed verification status into review_state only.
visibilityEmit
[handling.visibility || 'unspecified', dataset, dtype] -> 1.This is mandatory for API-side authority filtering and public-mode safety.
source_presenceEmit
[dataset, has_sources|no_sources, dtype] -> 1.2.
activity-v1Do not keep one ambiguous “documents by day” view.
by_time_basisFor every valid timestamp available on a document, emit one row with a bounded basis enum:
["added", YYYY, MM, DD, dataset, dtype]["updated", YYYY, MM, DD, dataset, dtype]["observed", ...]fromtemporal.observed_at["collected", ...]fromtemporal.collected_at["valid_from", ...]when present["valid_to", ...]when present["generated", ...]for canonical manifest/pass generation timestamps where the schema explicitly provides oneReduce
_count.No heuristic substitution: absence of a basis means no emitted row for that basis. Do not rewrite historical timestamps to make charts prettier.
This enables the Auto-Dig #2018 selector without full-corpus scans and preserves exact raw counts even when the UI uses log/symlog/aggregation presentation.
latest_updatedMap-only key
[date_updated, _id] -> {dataset,dtype}or a minimal fixed projection. Descending range query supplies latest activity without sorting the corpus in application memory.3.
graph-v1All relation fields must use canonical
datapaths with a narrowly tested compatibility fallback only for proven legacy records.outgoingFor each relation subject endpoint emit:
[subject_id, predicate, object_id, dataset, relation_id] -> null.incoming[object_id, predicate, subject_id, dataset, relation_id] -> null.Endpoint helper must support the canonical forms Auto-Dig already handles: string ID,
{id: ...}, and arrays.predicate_counts[dataset, predicate] -> 1, reduce_count.degreeEmit both directions using key
[entity_id, direction, predicate, dataset] -> 1, reduce_count.Callers can group by entity for topological ranking without scanning all relations.
reviewed_degreeOnly emit relations whose normalized review state is reviewed. This directly supports Auto-Dig's “reviewed graph” / top-connected-people behavior.
relation_stateEmit
[dataset, review_state, verification_status, predicate] -> 1for graph QA.Do not attempt transitive closure, path finding, contradiction detection, or entity resolution in CouchDB map/reduce. Those require graph/application logic.
4.
research-v1targets_by_stateFor
dtype === 'investigation-target', normalize state fromworkflow.research_status, thendata.status, then top-level status.Emit
[dataset, state, priority_bucket, recursion_depth, _id] -> null.Keep raw priority/score in a small value projection for frontier ranking; do not encode arbitrary prose.
targets_by_runEmit
[workflow.run_id || provenance.run_id, state, recursion_depth, _id] -> minimal target projection.target_countsEmit
[dataset, state, recursion_depth] -> 1, reduce_count.research_passesMap-only
[dataset, date_updated, _id] -> {run_id, unresolved_count, finding_count}.Counts are derived only from arrays present in the same research-pass document.
research_frontierFor each explicit unresolved target ID present in a research-pass, emit
[dataset, unresolved_target_id, pass_id] -> null.This indexes explicit recorded frontier state; it must not infer missing work from absence.
5.
evidence-v1source_domainFor each canonical source URL/URI embedded on a document, emit
[normalized_host, dataset, dtype] -> 1.Reduce
_count.Host normalization must be deterministic and shared/tested; malformed URLs go to an explicit invalid bucket or are skipped with fixture coverage.
source_kindEmit
[dataset, source.kind || 'unspecified', dtype] -> 1.evidence_shapeEmit
[dataset, dtype, evidence_count_bucket, source_count_bucket] -> 1.This supports corpus QA without trying to decide whether evidence is substantively sufficient.
confidenceEmit
[dataset, dtype, normalized_confidence_bucket] -> 1from canonicalassessment.confidencewhen numeric.No ranking claim is implied by the view.
6.
entities-v1by_nameFor person/org and other name-bearing entity dtypes, use canonical
data.name, falling back totitleand only then proven legacy name components. Emit a normalized search key plus_id, but never overwrite the canonical display string.aliasesEmit one row per explicit alias:
[normalized_alias, dtype, _id] -> canonical_display_name.This is for candidate lookup only, not automatic merging.
datasets_for_entityUse explicit canonical records/relations only. Do not infer same-person identity across different IDs from matching names.
7.
manifests-v1dataset_manifestMap dataset-manifest docs by
[dataset, generated_at, _id]with minimal value containingrecord_count,counts_by_dtype, schema versions, actor/pipeline/run ID, and declared coverage status.This gives operators a cheap way to compare manifest-declared materialization with live canonical counts, while treating discrepancies as a signal rather than silently trusting either side.
A — Adversarial review
Reject: one giant design document
CouchDB rebuilds indexes for views in a design doc when that design doc changes. Separate stable semantic families so changing experimental research projections does not force the core corpus index to rebuild. Keep manifest/versioning explicit.
Reject: custom reducers for rich objects
Prefer built-ins (
_count,_sum,_statsonly where truly needed). Reducers must stay associative/rereduce-safe and converge to small fixed output. PouchDB parity is a hard constraint.Reject: full-document view values
At 1.25M+ corpus scale this needlessly bloats B-trees. Map values must be
null, scalar, ID, or a small fixed projection. Consumers requestinclude_docswhen they need bodies.Reject: “conflict detector” map/reduce
Cross-document contradictions, duplicate identity adjudication, stale-vs-current semantics, and claim/fact conflicts are application/Prolog/graph jobs. Views should only expose the indexed evidence needed to perform them.
Reject: silently treating all dates as event time
The observed Auto-Dig pulse failure proves this is misleading. Time-basis is explicit and queryable.
Reject: making public-mode visibility filtering optional
A generic
/api/v1/viewsendpoint must not let an anonymous caller query a projection whose rows were built from non-public documents. Public API view access must be allowlisted and visibility-filtered or use public-only projections. Never rely on clients to filter returned rows.Reject: topic-specific views
No
flock_*,worldcoin_*,cloarida_*, etc. Those investigations prove the need for generic relation, lifecycle/time, evidence, target, and dataset projections.D — Decision gate
Implement in this order
P0 correctness
P1 corpus/dashboard
6.
core-v1;7.
activity-v1explicit time-basis view;8.
graph-v1predicate/degree/reviewed-degree;9.
research-v1target/pass/frontier projections.P2 evidence/operator
10.
evidence-v1;11.
entities-v1aliases/name lookup;12.
manifests-v1reconciliation surface.P3 API/local parity
13.
/api/v1/viewsallowlisted discovery/query contract;14. Quasar/PouchDB installation of the same manifest;
15. warm-up/status/migration and old-index cleanup tooling.
TDD gate
Tests must be written before each implementation slice.
Fixture sources
Copy/redact-to-minimal deterministic fixtures from actual canonical v0.9 Auto-Dig shapes, including:
data.name;data.subject/predicate/object;counts_by_dtypeand coverage status;bad_docsmust not flag;Required proofs
Success criteria
date_added.Non-goals
Architecture correction — generalized core only
Superseding any ambiguous interpretation of this issue:
starintel-serverowns only generalized, schema-level, reusable StarIntel projections that make sense across arbitrary valid v0.9 corpora.starintel-labs/starintel-infraunder infra issue #36.Examples that stay core: dtype/dataset counts, canonical relation incoming/outgoing/degree, explicit timestamp bases, generic verification/review/visibility, generic target/pass state where those are canonical dtypes, generic name/alias/source summaries.
Examples that move to infra packs: Flock lifecycle/sharing summaries, campaign-finance reconciliation, Palantir contract dashboards, per-deployment operator queues, dataset-specific materializations, and other domain/report-specific indexes.
The API registry must distinguish built-in core views from installed custom packs; installation alone must not make a custom view public.