@geraldmaron/construct 1.0.21 → 1.0.24
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/README.md +23 -7
- package/bin/construct +206 -228
- package/bin/construct-postinstall.mjs +25 -15
- package/lib/acp/server.mjs +113 -0
- package/lib/agent-instructions/inject.mjs +94 -0
- package/lib/auto-docs.mjs +10 -2
- package/lib/cli-commands.mjs +43 -15
- package/lib/comment-lint.mjs +115 -0
- package/lib/completions.mjs +7 -1
- package/lib/config/schema.mjs +4 -0
- package/lib/decisions/enforced-baseline.json +2 -0
- package/lib/docs-verify.mjs +15 -18
- package/lib/doctor/cli.mjs +8 -1
- package/lib/document-export.mjs +124 -0
- package/lib/embed/daemon.mjs +1 -1
- package/lib/embed/inbox.mjs +31 -10
- package/lib/embed/recommendation-store.mjs +7 -289
- package/lib/embed/reconcile.mjs +2 -2
- package/lib/features.mjs +11 -11
- package/lib/git-hooks-path.mjs +61 -0
- package/lib/home-namespace.mjs +60 -0
- package/lib/hooks/ci-status-check.mjs +62 -40
- package/lib/hooks/config-protection.mjs +4 -4
- package/lib/hooks/orchestration-dispatch-guard.mjs +153 -0
- package/lib/hooks/pre-push-gate.mjs +15 -6
- package/lib/hooks/session-reflect.mjs +5 -1
- package/lib/hooks/stop-notify.mjs +32 -17
- package/lib/hooks/stop-typecheck.mjs +7 -2
- package/lib/host-capabilities.mjs +24 -8
- package/lib/host-disposition.mjs +76 -0
- package/lib/ingest/provider-extract.mjs +1 -1
- package/lib/init-docs.mjs +1 -1
- package/lib/init-unified.mjs +320 -219
- package/lib/init-update.mjs +4 -84
- package/lib/init.mjs +9 -25
- package/lib/install/stage-project.mjs +8 -2
- package/lib/intake/git-queue.mjs +195 -0
- package/lib/intake/queue.mjs +9 -16
- package/lib/intent-classifier.mjs +1 -1
- package/lib/knowledge/search.mjs +52 -3
- package/lib/mcp/server.mjs +57 -14
- package/lib/mcp/tools/memory.mjs +2 -2
- package/lib/mcp/tools/orchestration-run.mjs +125 -0
- package/lib/mcp/tools/storage.mjs +2 -3
- package/lib/mcp-catalog.json +3 -3
- package/lib/mcp-manager.mjs +59 -3
- package/lib/model-registry.mjs +40 -33
- package/lib/observation-store.mjs +39 -163
- package/lib/opencode-config.mjs +1 -1
- package/lib/orchestration/events.mjs +66 -0
- package/lib/orchestration/runtime.mjs +72 -10
- package/lib/orchestration/worker.mjs +69 -21
- package/lib/orchestration-policy.mjs +27 -3
- package/lib/parity.mjs +46 -24
- package/lib/policy/unified-gates.mjs +96 -0
- package/lib/project-init-shared.mjs +0 -173
- package/lib/reconcile/adapter-prune.mjs +105 -0
- package/lib/reconcile/agent-instructions-rewrap.mjs +98 -0
- package/lib/reconcile/gitignore-coverage.mjs +88 -0
- package/lib/reconcile/index.mjs +169 -0
- package/lib/reconcile/legacy-doctrine-strip.mjs +139 -0
- package/lib/reconcile/legacy-guide-decommit.mjs +67 -0
- package/lib/reconcile/legacy-skills-cleanup.mjs +200 -0
- package/lib/reconcile/mcp-entry-reconcile.mjs +142 -0
- package/lib/runtime/uv-bootstrap.mjs +27 -3
- package/lib/schema-infer.mjs +16 -2
- package/lib/server/csrf.mjs +14 -2
- package/lib/server/index.mjs +95 -0
- package/lib/service-manager.mjs +59 -252
- package/lib/setup-prompts.mjs +2 -1
- package/lib/setup.mjs +104 -480
- package/lib/status.mjs +3 -6
- package/lib/storage/admin.mjs +48 -325
- package/lib/storage/backend.mjs +10 -43
- package/lib/storage/hybrid-query.mjs +15 -196
- package/lib/storage/sync.mjs +36 -177
- package/lib/storage/vector-client.mjs +256 -235
- package/lib/strategy-store.mjs +35 -286
- package/lib/template-registry.mjs +73 -0
- package/lib/term-format.mjs +75 -0
- package/lib/uninstall/uninstall.mjs +180 -7
- package/lib/worker/entrypoint.mjs +6 -14
- package/package.json +7 -6
- package/personas/construct.md +7 -8
- package/platforms/claude/settings.template.json +30 -11
- package/platforms/opencode/config.template.json +2 -2
- package/rules/common/neurodivergent-output.md +66 -0
- package/rules/common/tool-invisibility.md +37 -0
- package/scripts/sync-specialists.mjs +427 -107
- package/skills/operating/orchestration-reference.md +2 -16
- package/specialists/policy-inventory.json +14 -0
- package/specialists/prompts/cx-accessibility.md +2 -6
- package/specialists/prompts/cx-ai-engineer.md +0 -4
- package/specialists/prompts/cx-architect.md +3 -5
- package/specialists/prompts/cx-business-strategist.md +0 -5
- package/specialists/prompts/cx-data-analyst.md +0 -4
- package/specialists/prompts/cx-data-engineer.md +0 -4
- package/specialists/prompts/cx-debugger.md +2 -6
- package/specialists/prompts/cx-designer.md +0 -8
- package/specialists/prompts/cx-devil-advocate.md +2 -2
- package/specialists/prompts/cx-docs-keeper.md +0 -13
- package/specialists/prompts/cx-engineer.md +0 -13
- package/specialists/prompts/cx-evaluator.md +2 -2
- package/specialists/prompts/cx-explorer.md +4 -5
- package/specialists/prompts/cx-legal-compliance.md +4 -5
- package/specialists/prompts/cx-operations.md +0 -5
- package/specialists/prompts/cx-orchestrator.md +0 -4
- package/specialists/prompts/cx-platform-engineer.md +0 -8
- package/specialists/prompts/cx-product-manager.md +0 -8
- package/specialists/prompts/cx-qa.md +3 -12
- package/specialists/prompts/cx-rd-lead.md +0 -5
- package/specialists/prompts/cx-release-manager.md +0 -8
- package/specialists/prompts/cx-researcher.md +5 -29
- package/specialists/prompts/cx-reviewer.md +2 -6
- package/specialists/prompts/cx-security.md +2 -11
- package/specialists/prompts/cx-sre.md +0 -15
- package/specialists/prompts/cx-test-automation.md +0 -4
- package/specialists/prompts/cx-trace-reviewer.md +2 -2
- package/specialists/prompts/cx-ux-researcher.md +0 -4
- package/specialists/registry.json +28 -36
- package/templates/distribution/run.mjs +36 -7
- package/templates/docs/accessibility-audit.md +56 -0
- package/templates/docs/architecture-review.md +59 -0
- package/templates/docs/code-review-report.md +46 -0
- package/templates/docs/construct_guide.md +15 -15
- package/templates/docs/debug-investigation.md +53 -0
- package/templates/docs/qa-report.md +48 -0
- package/templates/docs/security-audit-report.md +48 -0
- package/templates/docs/task-packet.md +49 -0
- package/templates/docs/verdict.md +40 -0
- package/db/schema/001_init.sql +0 -40
- package/db/schema/002_pgvector.sql +0 -182
- package/db/schema/003_intake.sql +0 -47
- package/db/schema/003_observation_reconciliation.sql +0 -14
- package/db/schema/004_recommendations.sql +0 -46
- package/db/schema/005_strategy.sql +0 -21
- package/db/schema/006_graph.sql +0 -24
- package/db/schema/007_tags.sql +0 -30
- package/db/schema/008_skill_usage.sql +0 -24
- package/db/schema/009_scheduler.sql +0 -14
- package/db/schema/010_cx_scores.sql +0 -51
- package/lib/intake/postgres-queue.mjs +0 -240
- package/lib/server/static/index.html +0 -1
- package/lib/services/local-postgres.mjs +0 -15
- package/lib/storage/backup.mjs +0 -347
- package/lib/storage/migrations.mjs +0 -187
- package/lib/storage/postgres-backup.mjs +0 -124
- package/lib/storage/sql-store.mjs +0 -45
- package/lib/storage/store-version.mjs +0 -115
- package/lib/storage/vector-store.mjs +0 -100
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Security Audit Report: {scope-title}
|
|
2
|
+
|
|
3
|
+
- **Date**: {YYYY-MM-DD}
|
|
4
|
+
- **Auditor**: cx-security (or named human)
|
|
5
|
+
- **Scope**: {repo / module / PR / threat-model version}
|
|
6
|
+
- **Threat model**: {path or "n/a"}
|
|
7
|
+
- **Verdict**: SAFE | ISSUES_FOUND | BLOCKED
|
|
8
|
+
- **Status**: draft | final
|
|
9
|
+
|
|
10
|
+
<!--
|
|
11
|
+
Think like an attacker — the attack surface the developer didn't know existed. Every claim
|
|
12
|
+
cites a CVE, a code path (file:line), a known attack pattern, or a reproducible repro. A
|
|
13
|
+
suspected risk you can't pinpoint is a question, not a finding.
|
|
14
|
+
-->
|
|
15
|
+
|
|
16
|
+
## Executive summary
|
|
17
|
+
<!-- 2–4 sentences: what was audited, count of findings by severity, the single most important thing the reader needs to know. -->
|
|
18
|
+
|
|
19
|
+
## Audit categories covered
|
|
20
|
+
|
|
21
|
+
- [ ] Secrets (hardcoded API keys, tokens, credentials in source or config)
|
|
22
|
+
- [ ] AuthN / AuthZ (bypass paths, missing checks, JWT validation gaps, privilege escalation)
|
|
23
|
+
- [ ] Injection (SQL, command, LDAP, template, SSTI, prompt)
|
|
24
|
+
- [ ] Data exposure (PII in logs, verbose errors, overbroad permissions)
|
|
25
|
+
- [ ] Input validation (unvalidated user input reaching dangerous sinks)
|
|
26
|
+
- [ ] XSS / CSRF / SSRF
|
|
27
|
+
- [ ] Dependencies (known CVEs in direct dependencies)
|
|
28
|
+
- [ ] Cryptography (weak algorithms, hardcoded keys, insufficient entropy)
|
|
29
|
+
|
|
30
|
+
<!-- Tick boxes for what was actually examined. A category not ticked is not "passed" — it's "not audited." Say so. -->
|
|
31
|
+
|
|
32
|
+
## Findings
|
|
33
|
+
|
|
34
|
+
| Category | Severity | Location | Trigger | Evidence | Recommended Fix |
|
|
35
|
+
|---|---|---|---|---|---|
|
|
36
|
+
| {one of the 8 categories above} | critical / high / medium / low | `path/to/file.ext:NNN` | {how an attacker reaches it} | CVE-NNNN / repro / pattern | {minimum patch} |
|
|
37
|
+
|
|
38
|
+
## Remediation priority
|
|
39
|
+
<!-- Order the findings by what to fix first. Group critical/high together; medium/low can be backlogged with explicit acceptance. -->
|
|
40
|
+
|
|
41
|
+
## Out of scope
|
|
42
|
+
<!-- Audit boundaries: what the auditor did not look at, and why. Future audits may need to. -->
|
|
43
|
+
|
|
44
|
+
## Handoff
|
|
45
|
+
|
|
46
|
+
- code fix → `next:cx-engineer`
|
|
47
|
+
- platform / infrastructure fix → `next:cx-platform-engineer`
|
|
48
|
+
- review of remediation → `next:cx-reviewer`
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Task Packet: {goal}
|
|
2
|
+
|
|
3
|
+
- **Dispatched**: {YYYY-MM-DD HH:MM}
|
|
4
|
+
- **Source**: Construct
|
|
5
|
+
- **Target specialist**: cx-{role}
|
|
6
|
+
- **Track**: immediate | focused | orchestrated
|
|
7
|
+
- **Status**: dispatched | accepted | done | blocked | needs-main-input
|
|
8
|
+
|
|
9
|
+
<!--
|
|
10
|
+
The shape emitted by Construct when dispatching to a specialist. Mirrors the
|
|
11
|
+
`construct-to-orchestrator` contract in `specialists/contracts.json`. Required fields below
|
|
12
|
+
are validated by the contract — a packet missing any required field BLOCKED_CONTRACTs at
|
|
13
|
+
handoff. The specialist's response is one terminal state (DONE | BLOCKED | NEEDS_MAIN_INPUT),
|
|
14
|
+
referencing this packet by id.
|
|
15
|
+
-->
|
|
16
|
+
|
|
17
|
+
## Goal
|
|
18
|
+
<!-- One sentence: what success looks like, in the user's voice. Specific and falsifiable; an outcome the specialist can verify they hit. -->
|
|
19
|
+
|
|
20
|
+
## Intent
|
|
21
|
+
<!-- One of: research | implementation | investigation | evaluation | fix. The deterministic policy in lib/orchestration-policy.mjs:INTENT_CLASSES is the source of truth; do not invent new classes. -->
|
|
22
|
+
|
|
23
|
+
## Work category
|
|
24
|
+
<!-- One of: visual | deep | quick | writing | analysis. Drives the model tier. Authority: lib/orchestration-policy.mjs:WORK_CATEGORIES. -->
|
|
25
|
+
|
|
26
|
+
## Risk flags
|
|
27
|
+
<!-- Subset of: architecture, security, dataIntegrity, ui, docs, ai. Drives whether cx-devil-advocate joins the chain. Authority: lib/orchestration-policy.mjs:detectRiskFlags. -->
|
|
28
|
+
|
|
29
|
+
## Acceptance criteria
|
|
30
|
+
<!-- The conditions that, when all met, mean DONE. Each criterion is verifiable (a test, a check, an observable). Subjective criteria ("looks good") are not acceptable; restate them as observable behavior. -->
|
|
31
|
+
|
|
32
|
+
| # | Criterion | Verification |
|
|
33
|
+
|---|---|---|
|
|
34
|
+
| 1 | {restated, falsifiable} | {test name / check / observable} |
|
|
35
|
+
|
|
36
|
+
## Context
|
|
37
|
+
<!-- The minimum the specialist needs to start: file paths, prior decisions, related bd ids, intake packet id, the user's exact language. Link, don't paraphrase. -->
|
|
38
|
+
|
|
39
|
+
## Constraints
|
|
40
|
+
<!-- Hard limits the specialist must respect: budget, timeline, contract boundaries, parts of the system that are off-limits, prior decisions that are not being revisited. -->
|
|
41
|
+
|
|
42
|
+
## Handoff candidates
|
|
43
|
+
<!-- Specialists this packet may legitimately route to next. Defaults read from specialists/role-manifests.json → <role>.handoffCandidates. -->
|
|
44
|
+
|
|
45
|
+
## Approval gates
|
|
46
|
+
<!-- If the work crosses an approval boundary (commit, push, scope change, irreversible action), name it here. Construct must surface these to the user; the specialist must not bypass. -->
|
|
47
|
+
|
|
48
|
+
## Verification plan
|
|
49
|
+
<!-- How DONE will be confirmed before the chain closes: test runs, doctor checks, smoke tests, manual review. The specialist proves DONE against this plan, not against a vibe. -->
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Verdict: {decision-or-question}
|
|
2
|
+
|
|
3
|
+
- **Date**: {YYYY-MM-DD}
|
|
4
|
+
- **Author**: {specialist or named human}
|
|
5
|
+
- **Trigger**: {what asked for this verdict — bd id, intake packet, dispatch event}
|
|
6
|
+
- **Verdict**: APPROVE | REJECT | CONDITIONAL | INSUFFICIENT_EVIDENCE
|
|
7
|
+
- **Confidence**: high | medium | low
|
|
8
|
+
- **Status**: draft | final
|
|
9
|
+
|
|
10
|
+
<!--
|
|
11
|
+
A verdict is a structured judgment. Sources first, then assessment, then recommendation —
|
|
12
|
+
in that order, so the recommendation is traceable to the evidence. A verdict without
|
|
13
|
+
counter-evidence is not a verdict; it's a vote.
|
|
14
|
+
-->
|
|
15
|
+
|
|
16
|
+
## Context
|
|
17
|
+
<!-- What is being judged, and why now. One paragraph. Link to the request (bd id, intake id, PR) and the prior verdicts that bear on it. -->
|
|
18
|
+
|
|
19
|
+
## Evidence
|
|
20
|
+
|
|
21
|
+
| Claim | Source | Date | Reliability |
|
|
22
|
+
|---|---|---|---|
|
|
23
|
+
| {one specific, falsifiable statement} | `{path / URL / commit / trace id}` | {YYYY-MM-DD} | A–F × 1–6 (Admiralty) |
|
|
24
|
+
|
|
25
|
+
<!-- Separate observation from inference. "The CI run failed" is observation; "the failure means X" is inference. Label each row. -->
|
|
26
|
+
|
|
27
|
+
## Counter-evidence
|
|
28
|
+
<!-- The strongest disconfirming evidence. If you cannot articulate it, the verdict is INSUFFICIENT_EVIDENCE, not APPROVE. -->
|
|
29
|
+
|
|
30
|
+
## Assessment
|
|
31
|
+
|
|
32
|
+
- **Severity / impact**: {what is at stake if this is wrong}
|
|
33
|
+
- **Confidence**: high (A1–B1 sources) / medium (B2–C2) / low (≤C3 or contested)
|
|
34
|
+
- **Failure mode the verdict guards against**: {one sentence — the thing this verdict exists to prevent}
|
|
35
|
+
|
|
36
|
+
## Recommendation
|
|
37
|
+
<!-- The action that follows from the evidence. If CONDITIONAL, name the conditions that flip the verdict. If INSUFFICIENT_EVIDENCE, name the evidence threshold that would change it. -->
|
|
38
|
+
|
|
39
|
+
## Handoff
|
|
40
|
+
<!-- Where this verdict goes next. Bare `next:cx-<role>` form so it works across hosts. -->
|
package/db/schema/001_init.sql
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
create extension if not exists vector;
|
|
2
|
-
|
|
3
|
-
create table if not exists construct_documents (
|
|
4
|
-
id text primary key,
|
|
5
|
-
project text not null,
|
|
6
|
-
kind text not null,
|
|
7
|
-
title text not null,
|
|
8
|
-
summary text,
|
|
9
|
-
body text not null,
|
|
10
|
-
source_path text,
|
|
11
|
-
tags jsonb not null default '[]'::jsonb,
|
|
12
|
-
content_hash text not null,
|
|
13
|
-
updated_at timestamptz not null default now(),
|
|
14
|
-
created_at timestamptz not null default now()
|
|
15
|
-
);
|
|
16
|
-
|
|
17
|
-
create index if not exists construct_documents_project_kind_idx on construct_documents (project, kind);
|
|
18
|
-
create index if not exists construct_documents_content_hash_idx on construct_documents (content_hash);
|
|
19
|
-
|
|
20
|
-
create table if not exists construct_embeddings (
|
|
21
|
-
document_id text primary key references construct_documents(id) on delete cascade,
|
|
22
|
-
model text not null,
|
|
23
|
-
embedding double precision[] not null,
|
|
24
|
-
content_hash text not null,
|
|
25
|
-
updated_at timestamptz not null default now(),
|
|
26
|
-
created_at timestamptz not null default now()
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
create index if not exists construct_embeddings_model_idx on construct_embeddings (model);
|
|
30
|
-
|
|
31
|
-
create table if not exists construct_sync_runs (
|
|
32
|
-
id bigserial primary key,
|
|
33
|
-
project text not null,
|
|
34
|
-
source text not null,
|
|
35
|
-
documents_synced integer not null default 0,
|
|
36
|
-
embeddings_synced integer not null default 0,
|
|
37
|
-
status text not null,
|
|
38
|
-
note text,
|
|
39
|
-
created_at timestamptz not null default now()
|
|
40
|
-
);
|
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
-- db/schema/002_pgvector.sql
|
|
2
|
-
-- Enable pgvector extension and add vector-typed columns for semantic search.
|
|
3
|
-
-- This migration is idempotent — safe to run multiple times.
|
|
4
|
-
|
|
5
|
-
-- Enable pgvector extension
|
|
6
|
-
CREATE EXTENSION IF NOT EXISTS vector;
|
|
7
|
-
|
|
8
|
-
-- Add vector column to construct_embeddings if it doesn't exist yet
|
|
9
|
-
-- (The original 001_init.sql used double precision[], which we keep for backward compat)
|
|
10
|
-
DO $$
|
|
11
|
-
BEGIN
|
|
12
|
-
-- Check if embedding column is still array type and needs migration
|
|
13
|
-
IF EXISTS (
|
|
14
|
-
SELECT 1 FROM information_schema.columns
|
|
15
|
-
WHERE table_name = 'construct_embeddings'
|
|
16
|
-
AND column_name = 'embedding'
|
|
17
|
-
AND data_type = 'ARRAY'
|
|
18
|
-
) THEN
|
|
19
|
-
-- Add new vector column
|
|
20
|
-
ALTER TABLE construct_embeddings ADD COLUMN embedding_vec vector(384);
|
|
21
|
-
|
|
22
|
-
-- Copy data from old array column to new vector column
|
|
23
|
-
UPDATE construct_embeddings
|
|
24
|
-
SET embedding_vec = embedding::vector(384);
|
|
25
|
-
|
|
26
|
-
-- Drop old column and rename
|
|
27
|
-
ALTER TABLE construct_embeddings DROP COLUMN embedding;
|
|
28
|
-
ALTER TABLE construct_embeddings RENAME COLUMN embedding_vec TO embedding;
|
|
29
|
-
END IF;
|
|
30
|
-
END $$;
|
|
31
|
-
|
|
32
|
-
-- Ensure embedding column exists as vector type (for fresh installs)
|
|
33
|
-
ALTER TABLE construct_embeddings
|
|
34
|
-
ALTER COLUMN embedding TYPE vector(384) USING embedding::vector(384);
|
|
35
|
-
|
|
36
|
-
-- Create HNSW index for fast approximate nearest neighbor search
|
|
37
|
-
CREATE INDEX IF NOT EXISTS idx_embeddings_hnsw
|
|
38
|
-
ON construct_embeddings
|
|
39
|
-
USING hnsw (embedding vector_cosine_ops)
|
|
40
|
-
WITH (m = 16, ef_construction = 64);
|
|
41
|
-
|
|
42
|
-
-- Create observations table with vector support
|
|
43
|
-
CREATE TABLE IF NOT EXISTS construct_observations (
|
|
44
|
-
id text PRIMARY KEY,
|
|
45
|
-
project text NOT NULL,
|
|
46
|
-
role text NOT NULL,
|
|
47
|
-
category text NOT NULL,
|
|
48
|
-
summary text NOT NULL,
|
|
49
|
-
content text NOT NULL,
|
|
50
|
-
tags jsonb NOT NULL DEFAULT '[]'::jsonb,
|
|
51
|
-
confidence float NOT NULL DEFAULT 0.8,
|
|
52
|
-
source text,
|
|
53
|
-
git_sha text,
|
|
54
|
-
created_at timestamptz NOT NULL DEFAULT now(),
|
|
55
|
-
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
56
|
-
embedding vector(384)
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
-- Indexes for observations
|
|
60
|
-
CREATE INDEX IF NOT EXISTS idx_observations_project ON construct_observations(project);
|
|
61
|
-
CREATE INDEX IF NOT EXISTS idx_observations_category ON construct_observations(project, category);
|
|
62
|
-
CREATE INDEX IF NOT EXISTS idx_observations_created_at ON construct_observations(project, created_at DESC);
|
|
63
|
-
CREATE INDEX IF NOT EXISTS idx_observations_vector ON construct_observations USING hnsw (embedding vector_cosine_ops);
|
|
64
|
-
|
|
65
|
-
-- Create sessions table with vector support
|
|
66
|
-
CREATE TABLE IF NOT EXISTS construct_sessions (
|
|
67
|
-
id text PRIMARY KEY,
|
|
68
|
-
project text NOT NULL,
|
|
69
|
-
platform text,
|
|
70
|
-
summary text,
|
|
71
|
-
decisions jsonb DEFAULT '[]'::jsonb,
|
|
72
|
-
files_changed jsonb DEFAULT '[]'::jsonb,
|
|
73
|
-
open_questions jsonb DEFAULT '[]'::jsonb,
|
|
74
|
-
task_snapshot jsonb DEFAULT '[]'::jsonb,
|
|
75
|
-
status text DEFAULT 'active',
|
|
76
|
-
created_at timestamptz NOT NULL DEFAULT now(),
|
|
77
|
-
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
78
|
-
embedding vector(384)
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
CREATE INDEX IF NOT EXISTS idx_sessions_project ON construct_sessions(project);
|
|
82
|
-
CREATE INDEX IF NOT EXISTS idx_sessions_status ON construct_sessions(project, status);
|
|
83
|
-
CREATE INDEX IF NOT EXISTS idx_sessions_vector ON construct_sessions USING hnsw (embedding vector_cosine_ops);
|
|
84
|
-
|
|
85
|
-
-- Create entities table with vector support
|
|
86
|
-
CREATE TABLE IF NOT EXISTS construct_entities (
|
|
87
|
-
name text PRIMARY KEY,
|
|
88
|
-
type text NOT NULL,
|
|
89
|
-
summary text,
|
|
90
|
-
project text,
|
|
91
|
-
observation_ids jsonb DEFAULT '[]'::jsonb,
|
|
92
|
-
related_entities jsonb DEFAULT '[]'::jsonb,
|
|
93
|
-
created_at timestamptz NOT NULL DEFAULT now(),
|
|
94
|
-
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
95
|
-
last_seen timestamptz,
|
|
96
|
-
embedding vector(384)
|
|
97
|
-
);
|
|
98
|
-
|
|
99
|
-
CREATE INDEX IF NOT EXISTS idx_entities_type ON construct_entities(type);
|
|
100
|
-
CREATE INDEX IF NOT EXISTS idx_entities_project ON construct_entities(project);
|
|
101
|
-
CREATE INDEX IF NOT EXISTS idx_entities_vector ON construct_entities USING hnsw (embedding vector_cosine_ops);
|
|
102
|
-
|
|
103
|
-
-- Function for searching documents by embedding
|
|
104
|
-
CREATE OR REPLACE FUNCTION search_documents(
|
|
105
|
-
project_name text,
|
|
106
|
-
query_embedding vector(384),
|
|
107
|
-
match_limit int DEFAULT 10,
|
|
108
|
-
min_similarity float DEFAULT 0.3
|
|
109
|
-
)
|
|
110
|
-
RETURNS TABLE(
|
|
111
|
-
id text,
|
|
112
|
-
title text,
|
|
113
|
-
summary text,
|
|
114
|
-
body text,
|
|
115
|
-
source_path text,
|
|
116
|
-
tags jsonb,
|
|
117
|
-
kind text,
|
|
118
|
-
similarity float
|
|
119
|
-
) AS $$
|
|
120
|
-
BEGIN
|
|
121
|
-
RETURN QUERY
|
|
122
|
-
SELECT
|
|
123
|
-
d.id,
|
|
124
|
-
d.title,
|
|
125
|
-
d.summary,
|
|
126
|
-
d.body,
|
|
127
|
-
d.source_path,
|
|
128
|
-
d.tags,
|
|
129
|
-
d.kind,
|
|
130
|
-
1 - (e.embedding <=> query_embedding) AS similarity
|
|
131
|
-
FROM construct_documents d
|
|
132
|
-
JOIN construct_embeddings e ON d.id = e.document_id
|
|
133
|
-
WHERE d.project = project_name
|
|
134
|
-
AND 1 - (e.embedding <=> query_embedding) > min_similarity
|
|
135
|
-
ORDER BY e.embedding <=> query_embedding
|
|
136
|
-
LIMIT match_limit;
|
|
137
|
-
END;
|
|
138
|
-
$$ LANGUAGE plpgsql;
|
|
139
|
-
|
|
140
|
-
-- Function for searching observations by embedding
|
|
141
|
-
CREATE OR REPLACE FUNCTION search_observations(
|
|
142
|
-
project_name text,
|
|
143
|
-
query_embedding vector(384),
|
|
144
|
-
match_limit int DEFAULT 10,
|
|
145
|
-
min_similarity float DEFAULT 0.3,
|
|
146
|
-
filter_role text DEFAULT NULL,
|
|
147
|
-
filter_category text DEFAULT NULL
|
|
148
|
-
)
|
|
149
|
-
RETURNS TABLE(
|
|
150
|
-
id text,
|
|
151
|
-
role text,
|
|
152
|
-
category text,
|
|
153
|
-
summary text,
|
|
154
|
-
content text,
|
|
155
|
-
tags jsonb,
|
|
156
|
-
confidence float,
|
|
157
|
-
source text,
|
|
158
|
-
created_at timestamptz,
|
|
159
|
-
similarity float
|
|
160
|
-
) AS $$
|
|
161
|
-
BEGIN
|
|
162
|
-
RETURN QUERY
|
|
163
|
-
SELECT
|
|
164
|
-
o.id,
|
|
165
|
-
o.role,
|
|
166
|
-
o.category,
|
|
167
|
-
o.summary,
|
|
168
|
-
o.content,
|
|
169
|
-
o.tags,
|
|
170
|
-
o.confidence,
|
|
171
|
-
o.source,
|
|
172
|
-
o.created_at,
|
|
173
|
-
1 - (o.embedding <=> query_embedding) AS similarity
|
|
174
|
-
FROM construct_observations o
|
|
175
|
-
WHERE o.project = project_name
|
|
176
|
-
AND 1 - (o.embedding <=> query_embedding) > min_similarity
|
|
177
|
-
AND (filter_role IS NULL OR o.role = filter_role)
|
|
178
|
-
AND (filter_category IS NULL OR o.category = filter_category)
|
|
179
|
-
ORDER BY o.embedding <=> query_embedding
|
|
180
|
-
LIMIT match_limit;
|
|
181
|
-
END;
|
|
182
|
-
$$ LANGUAGE plpgsql;
|
package/db/schema/003_intake.sql
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
-- 003_intake.sql — R&D intake queue backing table for team and enterprise mode.
|
|
2
|
-
-- Solo mode uses the filesystem under .cx/intake/ instead.
|
|
3
|
-
--
|
|
4
|
-
-- Worker claim algorithm: SELECT ... FROM construct_intake_items
|
|
5
|
-
-- WHERE status = 'pending' ORDER BY created_at ASC FOR UPDATE SKIP LOCKED LIMIT 1;
|
|
6
|
-
-- then set status = 'claimed', claimed_by, claimed_at in the same transaction.
|
|
7
|
-
--
|
|
8
|
-
-- payload jsonb carries the rest of the intake packet (intake, triage,
|
|
9
|
-
-- suggestion, related, excerpt, query) so the on-disk shape and the
|
|
10
|
-
-- in-table shape stay aligned.
|
|
11
|
-
|
|
12
|
-
create table if not exists construct_intake_items (
|
|
13
|
-
id text primary key,
|
|
14
|
-
project text not null,
|
|
15
|
-
tenant_id text,
|
|
16
|
-
status text not null default 'pending',
|
|
17
|
-
intake_type text,
|
|
18
|
-
rd_stage text,
|
|
19
|
-
primary_owner text,
|
|
20
|
-
recommended_action text,
|
|
21
|
-
risk text,
|
|
22
|
-
requires_approval boolean not null default false,
|
|
23
|
-
confidence double precision,
|
|
24
|
-
payload jsonb not null,
|
|
25
|
-
created_at timestamptz not null default now(),
|
|
26
|
-
updated_at timestamptz not null default now(),
|
|
27
|
-
claimed_by text,
|
|
28
|
-
claimed_at timestamptz,
|
|
29
|
-
processed_at timestamptz,
|
|
30
|
-
processed_by text,
|
|
31
|
-
notes text,
|
|
32
|
-
skipped_at timestamptz,
|
|
33
|
-
skipped_by text,
|
|
34
|
-
skip_reason text
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
create index if not exists construct_intake_items_status_idx
|
|
38
|
-
on construct_intake_items(status, created_at);
|
|
39
|
-
|
|
40
|
-
create index if not exists construct_intake_items_owner_idx
|
|
41
|
-
on construct_intake_items(primary_owner, status, created_at);
|
|
42
|
-
|
|
43
|
-
create index if not exists construct_intake_items_payload_gin_idx
|
|
44
|
-
on construct_intake_items using gin(payload);
|
|
45
|
-
|
|
46
|
-
create index if not exists construct_intake_items_project_tenant_idx
|
|
47
|
-
on construct_intake_items(project, tenant_id, status);
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
-- db/schema/003_observation_reconciliation.sql
|
|
2
|
-
-- Stamp each observation with the content hash and embedding model that
|
|
3
|
-
-- produced its vector. An idempotent reconciliation pass uses (content_hash,
|
|
4
|
-
-- model) to find rows whose embedding is missing or stale — content edited, or
|
|
5
|
-
-- the embedding model changed — and re-embed only those, rather than relying on
|
|
6
|
-
-- the inline write at creation time always succeeding. Mirrors the
|
|
7
|
-
-- (content_hash, model) tracking construct_embeddings already carries for
|
|
8
|
-
-- documents. Fully idempotent: safe to re-apply.
|
|
9
|
-
|
|
10
|
-
ALTER TABLE construct_observations ADD COLUMN IF NOT EXISTS content_hash text;
|
|
11
|
-
ALTER TABLE construct_observations ADD COLUMN IF NOT EXISTS model text;
|
|
12
|
-
|
|
13
|
-
CREATE INDEX IF NOT EXISTS idx_observations_content_hash ON construct_observations (content_hash);
|
|
14
|
-
CREATE INDEX IF NOT EXISTS idx_observations_model ON construct_observations (model);
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
-- 004_recommendations.sql — Recommendations table for team and enterprise mode.
|
|
2
|
-
-- Solo mode uses the JSONL store under .cx/intake/ instead.
|
|
3
|
-
--
|
|
4
|
-
-- Dedup key is enforced at the (project, dedup_key) level — matches the in-memory
|
|
5
|
-
-- dedup contract in lib/embed/recommendation-store.mjs.
|
|
6
|
-
-- Active recommendations: dismissed_at IS NULL AND superseded_at IS NULL.
|
|
7
|
-
|
|
8
|
-
create table if not exists construct_recommendations (
|
|
9
|
-
id text primary key,
|
|
10
|
-
project text not null,
|
|
11
|
-
dedup_key text not null,
|
|
12
|
-
type text not null,
|
|
13
|
-
title text not null,
|
|
14
|
-
reason text,
|
|
15
|
-
lane text,
|
|
16
|
-
signal_count int not null default 1,
|
|
17
|
-
total_signal_count int not null default 1,
|
|
18
|
-
customer_impact int not null default 0,
|
|
19
|
-
recency_bonus int not null default 0,
|
|
20
|
-
strategic_bonus int not null default 0,
|
|
21
|
-
score float not null default 0,
|
|
22
|
-
priority text not null default 'P3',
|
|
23
|
-
source_signal_ids jsonb not null default '[]'::jsonb,
|
|
24
|
-
first_seen timestamptz not null default now(),
|
|
25
|
-
last_seen timestamptz not null default now(),
|
|
26
|
-
dismissed_at timestamptz,
|
|
27
|
-
dismiss_reason text,
|
|
28
|
-
superseded_at timestamptz,
|
|
29
|
-
superseded_by_id text,
|
|
30
|
-
suppressed_until timestamptz,
|
|
31
|
-
suppress_reason text,
|
|
32
|
-
updated_at timestamptz not null default now()
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
create unique index if not exists construct_recommendations_project_dedup_key_idx
|
|
36
|
-
on construct_recommendations (project, dedup_key);
|
|
37
|
-
|
|
38
|
-
create index if not exists construct_recommendations_priority_score_idx
|
|
39
|
-
on construct_recommendations (project, priority, score desc);
|
|
40
|
-
|
|
41
|
-
create index if not exists construct_recommendations_last_seen_idx
|
|
42
|
-
on construct_recommendations (project, last_seen desc);
|
|
43
|
-
|
|
44
|
-
create index if not exists construct_recommendations_active_idx
|
|
45
|
-
on construct_recommendations (project, dismissed_at)
|
|
46
|
-
where dismissed_at is null;
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
-- 005_strategy.sql — Product strategy store for team and enterprise mode.
|
|
2
|
-
-- Solo mode uses ~/.cx/strategy.md instead.
|
|
3
|
-
--
|
|
4
|
-
-- Each row is an immutable version snapshot. The latest version for a project
|
|
5
|
-
-- is the row with the highest version number (or max updated_at when equal).
|
|
6
|
-
-- Callers insert a new row on each write; old versions are retained for history.
|
|
7
|
-
|
|
8
|
-
create table if not exists construct_strategy (
|
|
9
|
-
id bigserial primary key,
|
|
10
|
-
project text not null,
|
|
11
|
-
content text not null,
|
|
12
|
-
version int not null default 1,
|
|
13
|
-
updated_at timestamptz not null default now(),
|
|
14
|
-
updated_by text
|
|
15
|
-
);
|
|
16
|
-
|
|
17
|
-
create unique index if not exists construct_strategy_project_version_idx
|
|
18
|
-
on construct_strategy (project, version);
|
|
19
|
-
|
|
20
|
-
create index if not exists construct_strategy_project_updated_at_idx
|
|
21
|
-
on construct_strategy (project, updated_at desc);
|
package/db/schema/006_graph.sql
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
-- 006_graph.sql. GraphRAG community columns for entities.
|
|
2
|
-
--
|
|
3
|
-
-- Phase C9 foundations. Adds community_id (label propagation output) and a
|
|
4
|
-
-- community summary table so the Pg-backed deployment can query communities
|
|
5
|
-
-- without a JSONL scan. Solo-mode JSONL remains the source of truth; this
|
|
6
|
-
-- table is the projection.
|
|
7
|
-
|
|
8
|
-
ALTER TABLE construct_entities
|
|
9
|
-
ADD COLUMN IF NOT EXISTS community_id text,
|
|
10
|
-
ADD COLUMN IF NOT EXISTS community_size int;
|
|
11
|
-
|
|
12
|
-
CREATE INDEX IF NOT EXISTS idx_entities_community ON construct_entities(project, community_id);
|
|
13
|
-
|
|
14
|
-
CREATE TABLE IF NOT EXISTS construct_entity_communities (
|
|
15
|
-
community_id text NOT NULL,
|
|
16
|
-
project text NOT NULL,
|
|
17
|
-
size int NOT NULL DEFAULT 0,
|
|
18
|
-
top_members jsonb DEFAULT '[]'::jsonb,
|
|
19
|
-
summary text,
|
|
20
|
-
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
21
|
-
PRIMARY KEY (project, community_id)
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
CREATE INDEX IF NOT EXISTS idx_communities_size ON construct_entity_communities(project, size DESC);
|
package/db/schema/007_tags.sql
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
-- Tag attribution and migration tracking for construct_documents.
|
|
2
|
-
-- The construct_documents.tags JSONB column already exists in 001_init.sql.
|
|
3
|
-
-- This migration adds GIN indexing, attribution history, and migration audit.
|
|
4
|
-
|
|
5
|
-
create index if not exists construct_documents_tags_gin_idx
|
|
6
|
-
on construct_documents using gin (tags jsonb_path_ops);
|
|
7
|
-
|
|
8
|
-
create table if not exists construct_tag_attribution (
|
|
9
|
-
document_id text not null references construct_documents(id) on delete cascade,
|
|
10
|
-
tag text not null,
|
|
11
|
-
source text not null, -- 'agent:classifier' | 'user' | 'curator'
|
|
12
|
-
confidence numeric,
|
|
13
|
-
applied_at timestamptz not null default now(),
|
|
14
|
-
migrated_from text,
|
|
15
|
-
primary key (document_id, tag, source)
|
|
16
|
-
);
|
|
17
|
-
create index if not exists construct_tag_attribution_tag_idx on construct_tag_attribution (tag);
|
|
18
|
-
|
|
19
|
-
create table if not exists construct_tag_migrations (
|
|
20
|
-
id text primary key,
|
|
21
|
-
from_tag text not null,
|
|
22
|
-
to_tag text,
|
|
23
|
-
executed_at timestamptz not null default now(),
|
|
24
|
-
executed_by text not null,
|
|
25
|
-
reason text not null,
|
|
26
|
-
doc_count_before integer not null,
|
|
27
|
-
doc_count_after integer,
|
|
28
|
-
reversible boolean not null default true,
|
|
29
|
-
rolled_back_at timestamptz
|
|
30
|
-
);
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
-- db/schema/008_skill_usage.sql — Per-skill invocation log.
|
|
2
|
-
--
|
|
3
|
-
-- Backs `construct skills usage/orphans/hot`. The local
|
|
4
|
-
-- JSONL path (~/.cx/skill-calls.jsonl) is primary in solo mode; this table is
|
|
5
|
-
-- primary in team/enterprise mode. One-time backfill via
|
|
6
|
-
-- `construct skills backfill-postgres`.
|
|
7
|
-
|
|
8
|
-
create table if not exists construct_skill_invocations (
|
|
9
|
-
id bigserial primary key,
|
|
10
|
-
ts timestamptz not null,
|
|
11
|
-
skill_id text not null,
|
|
12
|
-
source text not null, -- 'mcp' | 'role-preload' | 'prompt-composer' | …
|
|
13
|
-
agent_id text,
|
|
14
|
-
session_id text,
|
|
15
|
-
caller_context text,
|
|
16
|
-
latency_ms integer,
|
|
17
|
-
tokens_returned integer
|
|
18
|
-
);
|
|
19
|
-
|
|
20
|
-
create index if not exists construct_skill_invocations_skill_ts_idx
|
|
21
|
-
on construct_skill_invocations (skill_id, ts desc);
|
|
22
|
-
|
|
23
|
-
create index if not exists construct_skill_invocations_session_idx
|
|
24
|
-
on construct_skill_invocations (session_id);
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
-- Scheduler job state for team and enterprise deployment modes.
|
|
2
|
-
-- Solo mode uses native trigger files instead of this table.
|
|
3
|
-
-- The lock_holder column stores a short identifier for the process/host
|
|
4
|
-
-- that currently holds the advisory lock for a running job.
|
|
5
|
-
|
|
6
|
-
create table if not exists construct_scheduled_jobs (
|
|
7
|
-
id text primary key,
|
|
8
|
-
schedule text not null,
|
|
9
|
-
last_run_at timestamptz,
|
|
10
|
-
last_run_status text,
|
|
11
|
-
lock_holder text,
|
|
12
|
-
lock_acquired_at timestamptz,
|
|
13
|
-
created_at timestamptz not null default now()
|
|
14
|
-
);
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
-- db/schema/010_cx_scores.sql — Per-trace quality score log + skill quality correlation view.
|
|
2
|
-
--
|
|
3
|
-
-- Backs `construct skills correlate-quality`. Producer: lib/mcp/tools/telemetry.mjs
|
|
4
|
-
-- cxScore() writes a row here when CONSTRUCT_DB_URL (or DATABASE_URL) is set.
|
|
5
|
-
-- Local JSONL/observation paths remain primary in solo mode; this table is
|
|
6
|
-
-- primary in team/enterprise mode where multiple agents share a session
|
|
7
|
-
-- timeline and the dashboard reads aggregate correlations.
|
|
8
|
-
|
|
9
|
-
create table if not exists construct_cx_scores (
|
|
10
|
-
id bigserial primary key,
|
|
11
|
-
ts timestamptz not null,
|
|
12
|
-
trace_id text not null,
|
|
13
|
-
session_id text,
|
|
14
|
-
agent_id text,
|
|
15
|
-
name text not null default 'quality',
|
|
16
|
-
value numeric not null,
|
|
17
|
-
comment text
|
|
18
|
-
);
|
|
19
|
-
|
|
20
|
-
create index if not exists construct_cx_scores_trace_idx
|
|
21
|
-
on construct_cx_scores (trace_id);
|
|
22
|
-
|
|
23
|
-
create index if not exists construct_cx_scores_session_ts_idx
|
|
24
|
-
on construct_cx_scores (session_id, ts desc);
|
|
25
|
-
|
|
26
|
-
create index if not exists construct_cx_scores_agent_ts_idx
|
|
27
|
-
on construct_cx_scores (agent_id, ts desc);
|
|
28
|
-
|
|
29
|
-
-- Skill quality correlation view: joins skill invocations and quality
|
|
30
|
-
-- scores by session_id so a reader can ask "for sessions where skill X was
|
|
31
|
-
-- invoked, what's the median / mean / p10 quality score?" The view is a
|
|
32
|
-
-- simple aggregate over the past 90 days; queries that need a longer
|
|
33
|
-
-- window can pull straight from the underlying tables. Materialised view
|
|
34
|
-
-- not used here because the correlation surface is small and the freshness
|
|
35
|
-
-- expectation is "current session" not "yesterday's batch."
|
|
36
|
-
|
|
37
|
-
create or replace view construct_skill_quality_correlation as
|
|
38
|
-
select
|
|
39
|
-
inv.skill_id,
|
|
40
|
-
count(distinct inv.session_id) as sessions,
|
|
41
|
-
count(distinct sc.id) as score_count,
|
|
42
|
-
round(avg(sc.value)::numeric, 3) as mean_score,
|
|
43
|
-
round((percentile_cont(0.5) within group (order by sc.value))::numeric, 3) as median_score,
|
|
44
|
-
round((percentile_cont(0.10) within group (order by sc.value))::numeric, 3) as p10_score,
|
|
45
|
-
round((percentile_cont(0.90) within group (order by sc.value))::numeric, 3) as p90_score,
|
|
46
|
-
max(sc.ts) as last_scored_at
|
|
47
|
-
from construct_skill_invocations inv
|
|
48
|
-
inner join construct_cx_scores sc on sc.session_id = inv.session_id
|
|
49
|
-
where inv.ts > now() - interval '90 days'
|
|
50
|
-
and sc.ts > now() - interval '90 days'
|
|
51
|
-
group by inv.skill_id;
|