Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
6baa4fe
fix(console): unify manifest-driven source setup
tnunamak Aug 6, 2026
d26d13d
fix(console): clarify first-user copy
tnunamak Aug 6, 2026
c197e92
fix(slack): harden first-user setup
tnunamak Aug 6, 2026
f27de2f
test: add owner source-to-MCP closure oracle
tnunamak Aug 6, 2026
c0f64f4
fix(lifecycle): reconcile setup status automatically
tnunamak Aug 6, 2026
a3839ac
fix(console): close catalog setup truth gaps
tnunamak Aug 6, 2026
00af3da
fix: restore explicit run admission default
tnunamak Aug 7, 2026
c09722f
fix(console): make owner catalog authoritative
tnunamak Aug 7, 2026
c3f6bf2
fix(console): expose supported Explore sort direction
tnunamak Aug 7, 2026
ce09284
fix(runtime): close setup lifecycle authority
tnunamak Aug 7, 2026
e0948d9
refactor(runtime): remove lifecycle lint suppressions
tnunamak Aug 7, 2026
beede6b
fix(console): clarify first-user setup copy
tnunamak Aug 7, 2026
bdc8a15
refactor(console): inline provider_auth_connect disposition handling
tnunamak Aug 7, 2026
1951b8b
style(console): use a type-only catalog import
tnunamak Aug 7, 2026
ab4c0fa
fix(catalog): expose proven browser setup
tnunamak Aug 7, 2026
86027fc
fix(test): compare rendered source stream counts
tnunamak Aug 7, 2026
3b94075
chore(connectors): refresh Slack lint locations
tnunamak Aug 7, 2026
68dfa5d
fix(catalog): restore YNAB static-secret setup path
tnunamak Aug 7, 2026
885a2f8
fix(chase): fall back to semantic role for QFX Download button click
tnunamak Aug 7, 2026
5772774
fix(reddit): wait for the login form to attach before giving up
tnunamak Aug 7, 2026
de21676
fix(console,ri): close setup-status lifecycle gaps on PR #84
tnunamak Aug 7, 2026
30bbeda
fix(connectors): promote temporary setup bindings to durable kinds on…
tnunamak Aug 7, 2026
91bf921
fix(connectors): close revoke race in setup-binding promotion, extend…
tnunamak Aug 7, 2026
e6dfe9b
build(docker): bundle slackdump v4.4.2 (AGPL-3.0) in core-browser image
tnunamak Aug 7, 2026
6773810
fix(slack): update manifest rationale — slackdump v4.4.2 bundled in core
tnunamak Aug 7, 2026
aa9bf4d
test(docker): verify slackdump v4.4.2 bundled in core image
tnunamak Aug 7, 2026
dedeca9
fix(test): correct slackdump assertions and await all subtests
tnunamak Aug 7, 2026
a3a9aca
fix(agpl): use exact resolvable source tree URL per AGPL section 6(d)
tnunamak Aug 7, 2026
43d2f80
fix(health): preserve idle draft setup state
tnunamak Aug 7, 2026
26faf24
feat(collector-runner): add read-only onMessage observer tap
tnunamak Aug 7, 2026
96c82b4
feat(local-collector): guided setup, live progress, --sample, logout
tnunamak Aug 7, 2026
0bf7ee0
feat(cli): forward setup/connectors/logout through the collector shim
tnunamak Aug 7, 2026
912699c
feat(console): render setup as the primary device-exporter command
tnunamak Aug 7, 2026
36eddd9
docs(local-collector): document setup/--sample/logout, lead with guid…
tnunamak Aug 7, 2026
1e87521
fix(maps): mark Google Maps Data Portability as unlisted — only archi…
tnunamak Aug 7, 2026
fba4894
fix(sources): derive connector-based fallback names for unnamed sources
tnunamak Aug 7, 2026
233cb6a
style(display): apply canonical formatting
tnunamak Aug 7, 2026
a8614d3
fix(heb): resume OTP after delayed owner response
tnunamak Aug 7, 2026
0ee341e
fix(stream-display): prefer manifest display labels across UI surfaces
tnunamak Aug 7, 2026
02d0cc6
fix(explore): extend ManifestMetadata with stream display labels
tnunamak Aug 7, 2026
aa582cf
fix(display): reconcile source and stream labels
codex Aug 7, 2026
1473e91
fix(console): rely on automatic setup status updates
codex Aug 7, 2026
b4aea26
fix(ingest): reuse writer ownership across batches
tnunamak Aug 7, 2026
9c74134
fix(ingest): preserve per-record provenance ordering
tnunamak Aug 7, 2026
82aba2e
fix(mobile-keyboard): extend cache TTL to cover mount→first-tap window
tnunamak Aug 7, 2026
3827d23
test(friend-readiness): keep focused gates type-safe
codex Aug 7, 2026
70378a8
fix(mobile): options popover viewport clamp + new-tab link forewarning
tnunamak Aug 7, 2026
f379282
fix(onboarding): close residual interaction gaps
codex Aug 7, 2026
70d856f
fix(connectors): make static-secret setup idempotent
tnunamak Aug 7, 2026
81d8842
fix(connectors): mark hidden Maps Data Portability unproven
tnunamak Aug 7, 2026
bdfb4fe
fix(railway): restore core env template
tnunamak Aug 7, 2026
f274157
fix(cli): parse current npm pack metadata
tnunamak Aug 7, 2026
32749f0
fix(test-accounting): account for setup-binding postgres skip
tnunamak Aug 7, 2026
3712eff
fix(ri): align hidden provider readiness expectations
tnunamak Aug 7, 2026
eb1deb8
fix(connectors): allow legacy static-secret repair
tnunamak Aug 7, 2026
5e14b5f
test(accounting): bind integrated postgres skips
tnunamak Aug 7, 2026
091e66c
test(accounting): normalize explore postgres skips
tnunamak Aug 7, 2026
7225530
fix(sqlite): make spine event sequence backfill collision-safe
tnunamak Aug 7, 2026
af7ccf0
fix(ingest): release blob writers during batch indexing
tnunamak Aug 7, 2026
26c7dc6
fix(owner-agent): preserve shared blobs during connection delete
tnunamak Aug 7, 2026
73708a7
fix(runtime): terminalize cancelled runs without queue drain
tnunamak Aug 7, 2026
517dc2d
chore(lint): realign sequential-await locations
tnunamak Aug 7, 2026
f1ebe73
test(accounting): declare explore postgres skips
tnunamak Aug 7, 2026
8b3bef4
test(accounting): narrow postgres test URL
tnunamak Aug 7, 2026
98b017c
fix(console): consolidate ownerActionable authority to single field
tnunamak Aug 7, 2026
c085942
fix(ci): verify bundled Slack tooling
tnunamak Aug 7, 2026
6b7fb18
fix(runtime): fence run-bound ingest writes against post-cancel commits
tnunamak Aug 7, 2026
6a86737
fix(connectors): fail closed on active static-secret credential repla…
tnunamak Aug 7, 2026
c40831e
feat(server): add self-scoped device-exporter revoke endpoint
tnunamak Aug 7, 2026
58365f8
feat(polyfill-connectors): add LocalDeviceClient.selfRevoke()
tnunamak Aug 7, 2026
8ffa7da
fix(local-collector): logout revokes device credential before deletin…
tnunamak Aug 7, 2026
e211c15
test(local-collector): cover logout self-revoke and run SIGINT handling
tnunamak Aug 7, 2026
2c18b33
docs(local-collector): correct interrupt-safety and logout claims
tnunamak Aug 7, 2026
72eb0ad
fix(records): close delete/write TOCTOU with opt-in connection-admiss…
tnunamak Aug 7, 2026
ae24b1a
test(records): preserve run-fence proof under connection admission
tnunamak Aug 7, 2026
cd452c3
test(records): bound lifecycle admission opt-in
tnunamak Aug 7, 2026
53491f1
test(reference): pin static secret replacement error status
tnunamak Aug 7, 2026
3baf774
fix(test): retain child reporter output through close
tnunamak Aug 7, 2026
d311bfc
test(accounting): declare run-fence PostgreSQL skips
tnunamak Aug 7, 2026
e0863b1
style(accounting): apply canonical test formatting
tnunamak Aug 7, 2026
2c14f64
test(console): align setup support with fail-closed catalog
tnunamak Aug 7, 2026
bcad030
test(static-secret): derive concurrent-draft winner instead of hardco…
tnunamak Aug 7, 2026
22910f2
test(friend-journey): search seeded records on reused volumes
tnunamak Aug 7, 2026
fd1ba1c
chore: reconcile friend readiness with presentation updates
tnunamak Aug 7, 2026
b541b4a
chore(site): refresh generated specification metadata
tnunamak Aug 7, 2026
343f6ff
fix(console): use consistent Connect apps copy
tnunamak Aug 7, 2026
2d3930d
test(perf): align Connect apps route marker
tnunamak Aug 7, 2026
90e8513
fix(console): do not link scheduler gate decisions as syncs
tnunamak Aug 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion .github/workflows/docker-images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -148,11 +148,21 @@ jobs:
build-args: |
PDPP_REFERENCE_REVISION=${{ github.sha }}
labels: ${{ steps.meta.outputs.labels }}
load: ${{ matrix.image == 'core' }}
platforms: linux/amd64
push: false
tags: ${{ steps.meta.outputs.tags }}
tags: |
${{ steps.meta.outputs.tags }}
${{ matrix.image == 'core' && 'pdpp:ci-core-test' || '' }}
target: ${{ matrix.target }}

- name: Test core image (slackdump bundling, license, builder isolation)
if: (github.event_name != 'workflow_dispatch' || github.event.inputs.image == 'all' || github.event.inputs.image == 'core') && matrix.image == 'core'
env:
PDPP_CORE_IMAGE_TAG: pdpp:ci-core-test
run: |
node --test scripts/docker-slackdump-core-bundle.test.ts

publish:
name: publish ${{ matrix.image }}
if: github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/v')
Expand Down
63 changes: 62 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,49 @@ EXPOSE 7662 7663

CMD ["sh", "-c", "export AS_PORT=\"${PORT:-${AS_PORT:-7662}}\"; export PDPP_RS_URL=\"${PDPP_RS_URL:-http://127.0.0.1:${RS_PORT:-7663}}\"; exec node reference-implementation/server/index.ts"]

# Isolated slackdump (v4.4.2, AGPL-3.0) builder stage.
# Downloads pre-built tarball, verifies SHA256, extracts binary and license.
# Only the binary (not build deps or Go) is copied to final image.
FROM debian:bookworm-slim AS slackdump-builder

ARG TARGETARCH

WORKDIR /build

# Map Docker TARGETARCH to slackdump release tarball name
RUN case "${TARGETARCH}" in \
x86_64|amd64) SLACKDUMP_TARBALL="slackdump_Linux_x86_64.tar.gz"; SLACKDUMP_SHA256="e2f386b2af30b0ba0ae98973f6a053225fba7d7127a20ad196cfdd96bf601052" ;; \
arm64) SLACKDUMP_TARBALL="slackdump_Linux_arm64.tar.gz"; SLACKDUMP_SHA256="71d8b55b9132c0d39d6fe66e3542ee7d2ec6c032b7701928124c736611cc235e" ;; \
*) echo "Unsupported architecture: ${TARGETARCH}" >&2; exit 1 ;; \
esac && \
echo "${SLACKDUMP_TARBALL}" > /tmp/tarball.txt && \
echo "${SLACKDUMP_SHA256}" > /tmp/sha256.txt

# Install only ca-certificates and curl; minimal runtime
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates curl && \
rm -rf /var/lib/apt/lists/*

# Download from official GitHub release, verify SHA256, extract
RUN TARBALL=$(cat /tmp/tarball.txt) && \
EXPECTED_SHA=$(cat /tmp/sha256.txt) && \
curl -fsSL -o "${TARBALL}" "https://github.com/rusq/slackdump/releases/download/v4.4.2/${TARBALL}" && \
ACTUAL_SHA=$(sha256sum "${TARBALL}" | awk '{print $1}') && \
if [ "${EXPECTED_SHA}" != "${ACTUAL_SHA}" ]; then \
echo "SHA256 mismatch for ${TARBALL}" >&2; \
echo "Expected: ${EXPECTED_SHA}" >&2; \
echo "Actual: ${ACTUAL_SHA}" >&2; \
exit 1; \
fi && \
tar -xzf "${TARBALL}" && \
test -x slackdump && \
./slackdump version

# Download LICENSE and source reference from upstream
# AGPL section 6(d): Corresponding Source URL must resolve to exact versioned tree
RUN curl -fsSL -o LICENSE "https://raw.githubusercontent.com/rusq/slackdump/v4.4.2/LICENSE" && \
echo "https://github.com/rusq/slackdump/tree/v4.4.2" > SOURCE_URL && \
test -f LICENSE && test -s LICENSE

# Dedicated browsers stage. Patchright + bundled Chromium + (on amd64) Google
# Chrome stable + their apt deps are baked into a stage whose cache key is
# only the patchright version and target arch. This is independent of the
Expand Down Expand Up @@ -174,9 +217,14 @@ CMD ["node", "reference-implementation/server/index.ts"]
# tranche. See openspec/changes/split-public-site-and-operator-console.
FROM base AS console

# The console image is paired with the current reference implementation, whose
# merged-timeline contract supports direction=asc. Keep the explicit capability
# gate enabled for that pairing; an older external RS can still fail closed by
# setting PDPP_EXPLORE_TIMELINE_DIRECTION=0.
ENV NODE_ENV=production \
HOSTNAME=0.0.0.0 \
PORT=3000
PORT=3000 \
PDPP_EXPLORE_TIMELINE_DIRECTION=1

COPY --from=console-builder /app/apps/console/.next/standalone ./
COPY --from=console-builder /app/apps/console/.next/static ./apps/console/.next/static
Expand Down Expand Up @@ -222,6 +270,9 @@ ARG PDPP_REFERENCE_REVISION=unknown
# [[restart]] override). If this stage is ever deployed through a path with
# no restart policy, that deployment is the truthful gap to fix, not this
# flag.
# Core bundles the matching reference implementation, including the
# direction=asc merged-timeline read contract. Keep the UI capability gate
# explicit; an older external RS can still fail closed with =0.
ENV NODE_ENV=production \
HOSTNAME=0.0.0.0 \
PORT=3000 \
Expand All @@ -234,6 +285,7 @@ ENV NODE_ENV=production \
PDPP_BROWSER_PROFILE_ROOT=/var/lib/pdpp/browser-profiles \
PDPP_EMBEDDING_DOWNLOAD_ALLOWED=1 \
PDPP_EMBEDDING_CACHE_DIR=/var/lib/pdpp/transformers \
PDPP_EXPLORE_TIMELINE_DIRECTION=1 \
PDPP_REFERENCE_OPERATIONAL_DEFAULTS=1 \
PDPP_LOCAL_TRANSFORMER_SUPERVISOR_RESTART_CONTRACT=1 \
PDPP_RECONCILE_POLYFILL_MANIFESTS=1 \
Expand All @@ -245,6 +297,15 @@ COPY --from=console-builder /app/apps/console/.next/standalone /console
COPY --from=console-builder /app/apps/console/.next/static /console/apps/console/.next/static
COPY --from=console-builder /app/apps/console/public /console/apps/console/public

# Copy slackdump binary (AGPL-3.0, v4.4.2) from builder stage.
# Binary required by Slack connector; upstream: https://github.com/rusq/slackdump/blob/v4.4.2
COPY --from=slackdump-builder /build/slackdump /usr/local/bin/slackdump
COPY --from=slackdump-builder /build/LICENSE /usr/local/share/slackdump/LICENSE.agpl-3.0.txt
COPY --from=slackdump-builder /build/SOURCE_URL /usr/local/share/slackdump/SOURCE_URL

# Verify slackdump is executable and functional
RUN chmod +x /usr/local/bin/slackdump && /usr/local/bin/slackdump version

EXPOSE 3000

CMD ["node", "--import", "tsx", "/app/deploy/railway/core-supervisor.ts"]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright The PDP-Connect Contributors
// SPDX-License-Identifier: Apache-2.0

import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import test from "node:test";
import { fileURLToPath } from "node:url";

const SOURCE_SETUP_CATALOG_FILE = fileURLToPath(new URL("./source-setup-catalog.tsx", import.meta.url));

const EXTERNAL_DOCS = /externalDocs\.map/;
const NEW_TAB = /target="_blank"/;
const NEW_TAB_TITLE = /title="Opens in a new tab"/;
const NEW_TAB_COPY = /\(opens in a new tab\)/;
const NOREFERRER = /rel="noreferrer"/;

test("source-setup-catalog renders external documentation links with new-tab forewarning", async () => {
const src = await readFile(SOURCE_SETUP_CATALOG_FILE, "utf8");
assert.match(src, EXTERNAL_DOCS, "must render externalDocs links");
assert.match(src, NEW_TAB, 'all external links must have target="_blank"');
assert.match(src, NEW_TAB_COPY, "external documentation links must warn visibly before opening a new tab");
assert.match(
src,
NEW_TAB_TITLE,
'external documentation links must have title="Opens in a new tab" for accessibility/forewarning'
);
assert.match(src, NOREFERRER, 'all external links must have rel="noreferrer" for security');
});
71 changes: 56 additions & 15 deletions apps/console/src/app/(console)/components/source-setup-catalog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type { RefCountState } from "../lib/ref-client.ts";
import {
sourceSetupAction,
sourceSetupAvailability,
sourceSetupContext,
sourceSetupGuidance,
sourceSetupRank,
sourceSetupSecondaryAction,
Expand Down Expand Up @@ -93,7 +94,7 @@ function SourceAcquisitionPaths({ paths }: { paths: readonly ConnectorAcquisitio
const secondary = paths.filter((path) => !visibleLabels.has(path.label));
return (
<div className="mt-3" data-testid="source-acquisition-paths">
<p className="pdpp-eyebrow mb-1 text-muted-foreground">Acquisition paths</p>
<p className="pdpp-eyebrow mb-1 text-muted-foreground">Ways to add data</p>
<ul className="grid gap-2">
{visible.map((path) => (
<SourceAcquisitionPathRow key={`${path.posture}:${path.label}`} path={path} />
Expand All @@ -102,7 +103,7 @@ function SourceAcquisitionPaths({ paths }: { paths: readonly ConnectorAcquisitio
{secondary.length > 0 ? (
<details className="group mt-2">
<summary className="pdpp-caption cursor-pointer list-none text-muted-foreground underline decoration-dotted underline-offset-4 hover:text-foreground">
Other ways to add coverage
Other ways to add data
</summary>
<ul className="mt-2 grid gap-2">
{secondary.map((path) => (
Expand All @@ -116,30 +117,50 @@ function SourceAcquisitionPaths({ paths }: { paths: readonly ConnectorAcquisitio
}

function sourceMethodLine(entry: ConnectorCatalogEntry, existingSourceCount: number): string {
if (sourceSetupAvailability(entry) === "not_available_here") {
return "No proven setup path is available in this dashboard.";
}
if (entry.modality === "browser_bound" && entry.setupModality === "static_secret") {
return "Connect in a secure browser, with optional encrypted sign-in details for repair.";
return "Connect in a secure browser; interactive sign-in is valid, with optional saved details for repair.";
}
switch (entry.disposition) {
case "local_collector_enroll":
return "Local collector on the machine that has this data.";
return "Run the local collector on the machine that has this data.";
case "static_secret_connect":
return "Provider credential captured by this instance.";
return "Enter the provider credential for this account.";
case "provider_auth_connect":
return "Authorize this account through the provider.";
case "manual_upload_connect":
return existingSourceCount > 0
? `${existingSourceCount} existing ${existingSourceCount === 1 ? "source" : "sources"} can receive another export; choose on the import page.`
: "Owner-exported file import.";
case "provider_auth_deployment_blocked":
return "Server provider settings are required before account setup.";
return "This source needs provider authorization. Configure provider settings before adding an account.";
case "browser_collector_manual":
case "browser_bound_runbook":
return entry.disposition === "browser_collector_manual"
? "Connect account from a secure browser session."
: "Browser-backed setup is not packaged in this dashboard yet.";
: "Browser setup is not available in this dashboard yet.";
default:
return "No owner-usable setup path in this build.";
return "No setup path is available in this dashboard.";
}
}

function SourceSetupContext({ entry }: { entry: ConnectorCatalogEntry }) {
const context = sourceSetupContext(entry);
if (!context) {
return null;
}
return (
<p
className="pdpp-caption mt-2 rounded-md border border-border/60 bg-muted/20 px-2.5 py-2 text-muted-foreground"
data-testid="source-setup-context"
>
{context}
</p>
);
}

function sourceDetailHref(connectorKey: string, connectionId: string): string {
const params = new URLSearchParams({ connection_id: connectionId });
return `/sources/${encodeURIComponent(connectorKey)}?${params.toString()}`;
Expand Down Expand Up @@ -256,13 +277,14 @@ function SourceSetupCard({
</span>
</div>
<p className="pdpp-caption mt-1 text-muted-foreground">{sourceMethodLine(entry, existingSources.length)}</p>
<SourceSetupContext entry={entry} />
<ExistingSourceLinks connectorKey={entry.connectorKey} sources={existingSources} />
<SourceSetupDetails entry={entry} />
</div>
<div className="flex flex-col items-end justify-start gap-1">
{action ? (
<>
<span className="pdpp-eyebrow text-muted-foreground">Next</span>
<span className="pdpp-eyebrow text-muted-foreground">Next step</span>
<Link className={buttonVariants({ size: "sm", variant: "default" })} href={action.href}>
{action.label}
</Link>
Expand All @@ -277,7 +299,7 @@ function SourceSetupCard({
className="pdpp-caption rounded-md border border-border/70 bg-muted/20 px-2.5 py-1 text-muted-foreground"
data-testid="source-unavailable-fact"
>
{unavailable ? "Not available from this page" : "No primary action"}
{unavailable ? "Not available from this page" : "No setup path available here"}
</span>
)}
</div>
Expand All @@ -296,18 +318,37 @@ function ServerSetupSummary({ entries }: { entries: readonly ConnectorCatalogEnt
</summary>
<div className="mt-3 grid gap-3">
<p className="pdpp-caption text-muted-foreground">
These sources need provider app settings on this instance before an account can be added.
These sources need provider app settings on the instance before an account can be added. This dashboard shows
the missing requirements but does not edit provider applications here.
</p>
<ul className="grid gap-2">
{entries.map((entry) => (
<li className="flex flex-wrap items-center justify-between gap-2" key={entry.connectorKey}>
<li className="grid gap-2" key={entry.connectorKey}>
<div className="min-w-0">
<p className="pdpp-caption font-medium text-foreground">{entry.displayName}</p>
<p className="pdpp-caption text-muted-foreground">{sourceMethodLine(entry, 0)}</p>
<SourceSetupContext entry={entry} />
</div>
<Link className={buttonVariants({ size: "sm", variant: "ghost" })} href="/deployment">
Open server settings
</Link>
<p className="pdpp-caption text-muted-foreground" data-testid="server-setup-prerequisites">
{sourceSetupGuidance(entry)}
</p>
{entry.externalDocs.length > 0 ? (
<div className="flex flex-wrap gap-x-3 gap-y-1">
<span className="pdpp-caption text-muted-foreground">Provider documentation:</span>
{entry.externalDocs.map((doc) => (
<a
className="pdpp-caption text-foreground underline underline-offset-4"
href={doc.url}
key={`${entry.connectorKey}:${doc.url}`}
rel="noreferrer"
target="_blank"
title="Opens in a new tab"
>
{doc.label} (opens in a new tab)
</a>
))}
</div>
) : null}
</li>
))}
</ul>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const STATUS_FILE = `${HERE}../../connect/status/[connectionId]/page.tsx`;
const ONE_STATUS_AND_ACTION_COPY = /one status and one next action/;
const COMPACT_METHOD_LINE = /function sourceMethodLine/;
const SUPPORT_FACT_TEST_ID = /data-testid="source-support-fact"/;
const NEXT_COPY = />Next</;
const NEXT_COPY = />Next step</;
const GENERIC_SUPPORT_DETAIL_COPY = /Why this, and what to expect/;
const IMPORT_OPTIONS_DISCLOSURE = /Show import options/;
const EXISTING_SOURCE_REUSE = /data-testid="existing-source-reuse"/;
Expand All @@ -51,7 +51,7 @@ const STATUS_EXPLORE_LINK = /href=\{sourceRecordsHref\(status\)\}[\s\S]*Open in
const STATUS_SOURCE_DETAILS_LINK = /href=\{sourceDetailHref\(status\)\}[\s\S]*Source details/;
const CONNECTOR_LEVEL_STATUS_RECORDS_LINK = /href=\{`\/sources\/\$\{encodeURIComponent\(status\.connector_id\)\}`\}/;
const SERVER_SETUP_SUMMARY = /data-testid="server-setup-summary"/;
const MANIFEST_GENERATED_COPY = /generated from the connector manifest/;
const OWNER_SAFE_STORAGE_COPY = /stored for this source and is not exposed to connected apps or clients/;
const VALIDATES_BEFORE_COMMIT_COPY = /validates before committing/i;
const COVERAGE_RECEIPT_COPY = /coverage receipt|coverage provenance/i;
const PRIMARY_METHODS_IDENTIFIER = /primaryMethods/;
Expand Down Expand Up @@ -124,10 +124,10 @@ test("source catalog exposes exact existing-account links without turning the pi

// ── 2. Manual/upload page is a coverage-assistant start ─────────────────────

test("manual upload page is manifest-generated and uses validate-before-commit language", async () => {
test("manual upload page has owner-safe storage and validate-before-commit language", async () => {
const pageSrc = await readFile(MANUAL_UPLOAD_FILE, "utf8");
const formSrc = await readFile(MANUAL_UPLOAD_FORM_FILE, "utf8");
assert.match(pageSrc, MANIFEST_GENERATED_COPY);
assert.match(pageSrc, OWNER_SAFE_STORAGE_COPY);
// Validates before durable commit when a validator exists.
assert.match(formSrc, VALIDATES_BEFORE_COMMIT_COPY);
// It speaks of a durable receipt the owner can revisit.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,15 @@ const SOURCES_PAGE_STATUS_HELPER_IMPORT_RE =
/isActiveConnectorRunSummaryStatus[\s\S]*from "\.\.\/lib\/connector-run-summary-status\.ts"/;
const SOURCES_PAGE_STATUS_HELPER_CALL_RE = /isActiveConnectorRunSummaryStatus\(\s*s\.last_run\.status\s*\)/;
const LIST_CONNECTOR_MANIFESTS_RE = /listConnectorManifests\(\)/;
const BUILD_CONNECTOR_CATALOG_RE = /buildConnectorCatalog\(manifests\)/;
const LIST_OWNER_CONNECTOR_TEMPLATES_RE = /listOwnerConnectorTemplates\(\)/;
const BUILD_CONNECTOR_CATALOG_RE = /buildOwnerConnectorCatalog\(manifests, templates\)/;
const SOURCE_SETUP_CATALOG_RE = /<SourceSetupCatalog/;
const SOURCE_SETUP_SECTION_RE = /title="Add data"/;
const SOURCE_SEARCH_RE = /name="source_q"[\s\S]*?Search source name or connector key/;
const SOURCE_CARD_RE = /data-testid=\{`source-setup-\$\{entry\.connectorKey\}`\}/;
const SOURCE_ACQUISITION_PATHS_RE = /data-testid="source-acquisition-paths"/;
const SOURCE_ACQUISITION_PATH_RE = /data-testid="source-acquisition-path"/;
const OTHER_COVERAGE_PATHS_RE = /Other ways to add coverage/;
const OTHER_COVERAGE_PATHS_RE = /Other ways to add data/;
const UNAVAILABLE_GROUP_RE = /Sources not available from this page/;
const SERVER_SETUP_GROUP_RE = /Server settings needed before setup/;
const SERVER_SETUP_SUMMARY_RE = /data-testid="server-setup-summary"/;
Expand Down Expand Up @@ -130,6 +131,7 @@ test("Sources owns the add-source catalog route", async () => {
const page = await readFile(RECORDS_ADD_PAGE_FILE, "utf8");
const catalog = await readFile(SOURCE_SETUP_CATALOG_FILE, "utf8");
assert.match(page, LIST_CONNECTOR_MANIFESTS_RE);
assert.match(page, LIST_OWNER_CONNECTOR_TEMPLATES_RE);
assert.match(page, BUILD_CONNECTOR_CATALOG_RE);
assert.match(page, SOURCE_SETUP_CATALOG_RE);
assert.match(catalog, SOURCE_SETUP_SECTION_RE);
Expand Down Expand Up @@ -228,7 +230,7 @@ test("source setup presentation has no connector-specific copy or examples", asy
assert.doesNotMatch(src, FORBIDDEN_DEV_STRINGS_RE);
});

// ── 4. "Connect AI apps" is a separate read-access surface ──────────────────
// ── 4. "Connect apps" is a separate read-access surface ─────────────────────

test("the nav names the inbound client surface 'Connect apps', distinct from Sources", async () => {
const src = await readFile(SHELL_FILE, "utf8");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,7 @@ function RelationshipsBlock({
))}
</div>
) : (
<p className="rr-stand-empty">
No grant is out. Nothing is shared — only you and what you've given a token read this server.
</p>
<p className="rr-stand-empty">No grants are active. No connected app can read this instance yet.</p>
)}
</section>
);
Expand Down
Loading
Loading