The repository contains Social Datanode — a React, Vite, and TypeScript social graph
board app backed by Supabase. For an AI-friendly canonical summary, read
AI_CONTEXT.md first.
Current product behavior (code-aligned):
- Hash routes: landing (
#), board (#board), developer docs (#docs), contact (#contact), privacy (#privacy). - Board opens on a blank graph: one central
Youcircle only (no demo seed). - Canvas 2D board with circles, people, curved links, pan/zoom, edit/select/pan tool modes.
- Create via circle context menu, double-tap, and connector drag-to-empty.
- Inspector for rename, notes, connections, circle styling, delete (circles require confirmation in the inspector).
- Settings: LinkedIn ZIP import (+ sync guide), account sign-in/out, Agent API keys, graph
import/export/clear. Anonymous users see matching
!badges on the gear and LinkedIn guide?until the guide is opened once. - Toolbar search: local ranked search, signed-in smart search, LinkedIn profile URL import.
- Signed-in persistence: Supabase
user_graphs.graphwith revision-checked saves and Realtime sync. Anonymous:localStorage. - Agent access: revocable tokens,
graph-apiEdge Function, CLI, MCP server. - No multiplayer, drawing tools, or sticky notes.
Out of scope (not built): real-time collaboration/presence, whiteboard drawing, demo mode, global theme toggle, stress-test UI panel.
Install dependencies from the lockfile:
npm ciCopy the local environment example when working with auth, synced boards, or LinkedIn profile enrichment:
cp .env.example .env.localRequired Vite variables:
VITE_SUPABASE_URLVITE_SUPABASE_PUBLISHABLE_KEYorVITE_SUPABASE_ANON_KEY
Recommended teammate setup:
VITE_SUPABASE_URL=https://lycfoukfoesobeuumuad.supabase.co
VITE_SUPABASE_PUBLISHABLE_KEY=sb_publishable_l_x_y5rxdhL8Sd1ZE3QXag_lOCtr_M9
VITE_SUPABASE_ANON_KEY=If VITE_SUPABASE_PUBLISHABLE_KEY is present, VITE_SUPABASE_ANON_KEY may stay empty.
VITE_SUPABASE_ANON_KEY is still supported for older local setups, but new local environments should use the Supabase publishable key.
Required Supabase Edge Function secret for the graph API:
SUPABASE_SERVICE_ROLE_KEY
Keep it only in Supabase Edge Function secrets or local Supabase function env files. It is
used by graph-api to verify hashed agent tokens and apply graph writes for the resolved
owner user.
Everything else the teammate needs is already in the repository:
- application code
- the Supabase migration
.env.example- project documentation
The Google OAuth client secret is not needed in the app because it stays in the Supabase Dashboard.
Start the development server:
npm run devSigned-in users can create revocable agent tokens from Settings -> Agent API. The token is shown once and maps to that user's graph only.
Use the generated values like this:
export DATANODE_API_URL=https://<project-ref>.supabase.co/functions/v1/graph-api/v1
export DATANODE_API_TOKEN=dn_live_<token>CLI examples:
Using on-the-fly npx from anywhere (no clone needed):
npx -y --package github:TimofeySukh/hackathon datanode-cli meta
npx -y --package github:TimofeySukh/hackathon datanode-cli search "alice"
npx -y --package github:TimofeySukh/hackathon datanode-cli circles
npx -y --package github:TimofeySukh/hackathon datanode-cli people:add <circle-id> "Alice Chen" "Met at conference"
npx -y --package github:TimofeySukh/hackathon datanode-cli operations:run ./operations.jsonOr install globally:
npm install -g github:TimofeySukh/hackathon
datanode-cli search "alice"Or from a local repository checkout:
npm run datanode:cli -- meta
npm run datanode:cli -- search "alice"
npm run datanode:cli -- circles
npm run datanode:cli -- people:add <circle-id> "Alice Chen" "Met at conference"MCP config:
{
"mcpServers": {
"datanode": {
"command": "npx",
"args": ["-y", "github:TimofeySukh/hackathon"],
"env": {
"DATANODE_API_URL": "https://<project-ref>.supabase.co/functions/v1/graph-api/v1",
"DATANODE_API_TOKEN": "dn_live_<token>"
}
}
}
}All writes require the current graph revision. The CLI and MCP server read it before
writing; if another tab or agent saves first, the API returns 409 Conflict instead of
overwriting data.
MCP tool results are returned as a structured JSON envelope with status, summary,
data, and next_valid_actions. The MCP tool registry exposes risk metadata and uses
strict argument schemas. Agents can call list_capabilities to inspect a compact
risk-aware tool list before choosing a detailed graph tool. For large, experimental, bulk,
or destructive graph changes, agents should create a backup with export_graph or ask the
user for confirmation before calling replacement, reset, or broad cleanup tools.
For private LinkedIn export analysis that should stay outside Supabase, MCP, and the
authenticated graph API, use the local read-only JSONL search tool. It searches compact
people-for-llm.jsonl exports and returns exact person ids plus compact notes under a
token budget.
npm run --silent linkedin:agent-search -- stats
npm run --silent linkedin:agent-search -- search "founder agile" --budget-tokens 30000
npm run --silent linkedin:agent-search -- search "role:coach circle:Novo" --limit 100
npm run --silent linkedin:agent-search -- search "founder" \
--data /Users/velizard/Downloads/linkedin-graph-export-2026-06-14-basic/people-for-llm.jsonlThe default data file is the newest
~/Downloads/linkedin-graph-export-*/people-for-llm.jsonl. Results include a next
command when a large group needs paging. Keep the default 30,000 token budget for normal
LLM context assembly; use up to 50,000 only when the target model context can afford it.
Large import testing must stay out of production user accounts and production data.
Use a separate Supabase project for staging or load tests. Keep that project on the same migration set as production, but do not require it to mirror production data or capacity one-for-one. The goal is to verify import shape, batching, RLS assumptions, persistence size, and browser responsiveness without polluting real accounts.
Default local load checks:
npm run test:loadThis runs:
npm run test:db-load -- --people 3000 --connections 3000npm run test:ui-import -- --people 3000npm run test:ui-import:persistence
test:db-load is dry-run by default. It builds a synthetic graph with 3,000 people and
3,000 connections, serializes it in the same shape stored in user_graphs.graph, and
prints payload size without writing to Supabase.
To write against a staging Supabase project, set HACKATHON_LOAD_TEST_SUPABASE_URL
and HACKATHON_LOAD_TEST_SUPABASE_SERVICE_ROLE_KEY, then opt in explicitly:
HACKATHON_ALLOW_DATABASE_LOAD_TEST=true npm run test:db-load -- --write --cleanupSafety rules for database load writes:
- The script refuses to write to the URL in
.env.production. - The script requires
HACKATHON_ALLOW_DATABASE_LOAD_TEST=true. - The script creates an isolated Supabase Auth user for the run.
- The script writes one
user_graphsrow for that user and reads it back. - Pass
--cleanupto delete the test user after the read-back check; theuser_graphsrow is removed byon delete cascade.
Useful database load overrides:
npm run test:db-load -- --people 5000 --connections 5000
HACKATHON_ALLOW_DATABASE_LOAD_TEST=true npm run test:db-load -- --people 5000 --connections 5000 --write --cleanuptest:ui-import starts Vite on an isolated local port, opens Chromium through Playwright,
uploads a generated LinkedIn ZIP through the real settings-panel file input, and measures
event-loop lag while the import runs. The test fails when the browser is blocked longer
than the configured threshold.
test:ui-import:persistence is the local import persistence check:
npm run test:ui-import:persistenceThis starts a local mock Supabase graph API and a Vite app with VITE_E2E_FAKE_AUTH=true.
It does not touch production Supabase. The browser imports a generated LinkedIn ZIP, reloads
the board, then imports a board graph JSON and reloads again. The test fails unless both
imports are written to the mock graph API and survive reload.
Production auth email uses Supabase custom SMTP pointed at Resend. Keep the Resend API key only in the Supabase Dashboard SMTP password field; do not commit it to the repo.
Current SMTP settings:
Sender email: noreply@datanode.live
Sender name: DataNode
Host: smtp.resend.com
Port: 465
Username: resend
Password: <Resend API key>
When testing reset emails, wait at least 60 seconds before requesting a second email for the same user. The current Supabase Auth SMTP settings enforce a 60-second interval between emails sent to the same recipient.
Useful UI responsiveness overrides:
npm run test:ui-import -- --people 5000 --max-lag-ms 1500
npm run test:ui-import -- --url http://127.0.0.1:5173If Playwright's Chromium is not installed on the machine:
npx playwright install chromiumThe social.datanode.live deployment uses a pull-based production promotion flow.
The home server polls GitHub from cron, so it does not need a public IP address,
port forwarding, inbound SSH, or GitHub repository deploy secrets.
Production does not deploy every push to main. A GitHub Actions manual workflow
promotes a reviewed branch, tag, or commit SHA to the production branch. The server
then notices that production changed and deploys it from inside the home network.
Files:
.github/workflows/deploy-social-datanode-live.ymldeploy/social-datanode-live/auto-deploy/social-datanode-live-autodeploy.shdeploy/social-datanode-live/auto-deploy/social-datanode-live-autodeploy.cron
What it does:
- keeps deploy control in GitHub without requiring GitHub to SSH into the server
- promotes a selected ref to
productionthrough a manual workflow dispatch - checks
productionon GitHub every 3 minutes from cron - exits immediately when the remote commit did not change
- only runs
npm ci,npm run build, anddocker compose up -d --buildwhenproductionchanged - stores the last deployed commit SHA on the server to avoid unnecessary rebuilds
- loads optional build-time Vite variables from
$HOME/apps/social-datanode-live-autodeploy/deploy.envbeforenpm run build
Create the server-only build env file before the first production build:
cat > ~/apps/social-datanode-live-autodeploy/deploy.env <<'EOF'
VITE_SUPABASE_URL=https://lycfoukfoesobeuumuad.supabase.co
VITE_SUPABASE_PUBLISHABLE_KEY=<publishable-key-from-supabase>
VITE_SUPABASE_ANON_KEY=
EOFBefore the first server run, create or update the production branch with the promotion
workflow after this workflow file exists on main:
gh workflow run deploy-social-datanode-live.yml --ref main -f ref=mainSuggested server install for user egg:
mkdir -p ~/.local/bin ~/.local/share/social-datanode-live-autodeploy
install -m 755 deploy/social-datanode-live/auto-deploy/social-datanode-live-autodeploy.sh ~/.local/bin/social-datanode-live-autodeploy
crontab -l > /tmp/current-crontab 2>/dev/null || true
grep -v 'social-datanode-live-autodeploy' /tmp/current-crontab > /tmp/next-crontab || true
cat deploy/social-datanode-live/auto-deploy/social-datanode-live-autodeploy.cron >> /tmp/next-crontab
crontab /tmp/next-crontab
rm -f /tmp/current-crontab /tmp/next-crontab
~/.local/bin/social-datanode-live-autodeployPromote later releases after they are ready:
gh workflow run deploy-social-datanode-live.yml --ref main -f ref=mainPromote a specific commit SHA:
gh workflow run deploy-social-datanode-live.yml --ref main -f ref=<commit-sha>Check status:
crontab -l
tail -n 100 ~/.local/share/social-datanode-live-autodeploy/cron.log
gh run list --workflow deploy-social-datanode-live.yml --limit 5test.social.datanode.live is prepared as a parallel DigitalOcean App Platform target.
It does not replace the home-server social.datanode.live deployment and it does not
deploy automatically on push.
Files:
.github/workflows/deploy-datanode-digitalocean-test.yml.github/workflows/shutdown-datanode-digitalocean-test.yml.do/datanode-test.yaml.templatedeploy/digitalocean-app-platform/Dockerfile
What it does:
- deploys only from a manual GitHub Actions
workflow_dispatch - builds a single App Platform service from the public Git repository
- uses the smallest fixed shared container,
apps-s-1vcpu-0.5gb - reuses the production nginx config so security headers, cache behavior, and SPA fallback match the home-server deployment
- keeps Supabase as the backend; no DigitalOcean managed database is provisioned
- keeps
social.datanode.liveon the existing home-server promotion path until DNS and Supabase/Google auth settings are intentionally switched
Required GitHub repository secret:
DIGITALOCEAN_ACCESS_TOKEN
Required GitHub repository variables:
DATANODE_TEST_DOMAIN=test.social.datanode.live
VITE_SUPABASE_URL=<production Supabase URL>
VITE_SUPABASE_PUBLISHABLE_KEY=<production Supabase publishable key>
After the first successful create, store the printed DigitalOcean app id as:
DIGITALOCEAN_DATANODE_TEST_APP_ID=<app-id>
First deploy:
gh workflow run deploy-datanode-digitalocean-test.yml \
--ref main \
-f branch=main \
-f create_app=trueLater test deploys:
gh workflow run deploy-datanode-digitalocean-test.yml \
--ref main \
-f branch=mainShutdown the DigitalOcean test app so App Platform billing stops:
gh workflow run shutdown-datanode-digitalocean-test.yml --ref mainDNS:
- In DigitalOcean App Platform, inspect the app's domain status and default ingress.
- In Cloudflare, create
test.social.datanode.liveas a CNAME to the DigitalOcean target shown for the app domain. - Keep
social.datanode.liveunchanged until the DigitalOcean app has been validated.
Auth configuration required before sign-in works on the test domain:
- Supabase Auth URL Configuration: add
https://test.social.datanode.liveto the additional redirect URLs. Supabase requires redirect destinations to match the allow list. - Google OAuth Web Client: add
https://test.social.datanode.liveto Authorized JavaScript origins. The Supabase Google provider callback URL remains the Supabase callback URL from the dashboard.
To make DigitalOcean the default production path later:
- Add
social.datanode.liveto the DigitalOcean app spec domains. - Add
https://social.datanode.liveto Supabase Auth and Google OAuth settings if it is not already present. - Move the Cloudflare DNS record for
social.datanode.livefrom the existing cloudflared tunnel target to the DigitalOcean App Platform target. - Keep the home-server cron disabled only after the DigitalOcean domain is live and verified.
datanode-test is prepared as an Azure Static Web Apps Free target. It does not replace
the home-server social.datanode.live deployment and it does not deploy automatically on
push.
Azure resource:
Subscription: Azure subscription 1
Resource group: datanode-hosting-rg
Static Web App: datanode-test
SKU: Free
Location: Central US
Default host: salmon-smoke-003804210.7.azurestaticapps.net
Files:
.github/workflows/deploy-datanode-azure-test.ymlpublic/staticwebapp.config.json
What it does:
- deploys only from a manual GitHub Actions
workflow_dispatch - builds the Vite app with GitHub repository variables
VITE_SUPABASE_URLandVITE_SUPABASE_PUBLISHABLE_KEY - uploads the built
dist/directory to Azure Static Web Apps - uses
staticwebapp.config.jsonfor SPA fallback and security headers - keeps Supabase as the backend; no Azure database is provisioned
Required GitHub repository secret:
AZURE_STATIC_WEB_APPS_API_TOKEN
Deploy:
gh workflow run deploy-datanode-azure-test.yml --ref main -f ref=mainDNS for an Azure test hostname:
test.social.datanode.live CNAME salmon-smoke-003804210.7.azurestaticapps.net
Auth configuration required before sign-in works on the Azure host:
- Supabase Auth URL Configuration: add
https://salmon-smoke-003804210.7.azurestaticapps.netandhttps://salmon-smoke-003804210.7.azurestaticapps.net/**to the additional redirect URLs. - Google OAuth Web Client: add
https://salmon-smoke-003804210.7.azurestaticapps.netto Authorized JavaScript origins. The Supabase Google provider callback URL remains the Supabase callback URL from the dashboard.
Vite listens on all network interfaces in this repository, so it prints both a local URL and a network URL in the terminal. Open either URL in a browser.
For other devices on the same local network, open the printed network URL, for example:
http://10.29.0.117:5173
Other-device checklist:
- Keep the host machine and the other device on the same Wi-Fi or wired LAN.
- Start the app with
npm run devon the host machine. - Open the network URL from the other device.
- If macOS shows a firewall prompt for Node.js, allow incoming connections.
If the Supabase variables are missing, the app still opens as an editable anonymous local board and disables Google sign-in.
Teammate quick-start:
-
Pull the latest
main:git pull --ff-only npm ci
-
Create
.env.local:cp .env.example .env.local
-
Fill in
.env.local:VITE_SUPABASE_URL=https://lycfoukfoesobeuumuad.supabase.co VITE_SUPABASE_PUBLISHABLE_KEY=sb_publishable_l_x_y5rxdhL8Sd1ZE3QXag_lOCtr_M9 VITE_SUPABASE_ANON_KEY=
-
Run the project:
npm run dev
-
Open the URL printed by Vite.
Apply the database migration in supabase/migrations/ to the target Supabase project.
For local Supabase CLI workflows:
supabase db pushFor hosted dashboard workflows, paste and run the migration SQL in the Supabase SQL editor.
This repository includes Supabase Edge Functions in supabase/functions/.
Deploy the active Edge Functions before testing signed-in LinkedIn enrichment:
supabase functions deploy enrich-linkedin-profile
supabase functions deploy enrich-linkedin-archivesupabase/config.toml sets LinkedIn enrichment functions to verify_jwt = false at the Supabase gateway because each function performs its own user-token validation with supabase.auth.getUser(). Do not remove the in-function authorization check.
Required LinkedIn enrichment secret:
supabase secrets set LINKEDIN_ENRICHMENT_API_KEY=your-linkedin-enrichment-provider-api-keyRequired OpenRouter secrets for signed-in LinkedIn archive AI enrichment:
supabase secrets set OPENROUTER_API_KEY=your-openrouter-key
supabase secrets set OPENROUTER_MODEL=deepseek/deepseek-chat-v3-0324OPENROUTER_MODEL is optional; all archive enrichment tasks use the same DeepSeek model by
default. The archive enrichment function sends message, invitation, and post excerpts only
for transient LLM processing and returns derived notes; raw archive text is not persisted
in user_graphs.
Required secrets for signed-in natural-language smart search (POST /v1/search/smart):
supabase secrets set AI_SEARCH_API_KEY=your-neuraldeep-sk-key
supabase secrets set AI_SEARCH_API_BASE_URL=https://api.neuraldeep.ru/v1
supabase secrets set AI_SEARCH_MODEL=qwen3.6-35b-a3b-noreasonProvider docs: NeuralDeep. Default model qwen3.6-35b-a3b-noreason is the fastest
MoE chat model on the free tier (DaisyGPT / Qwen 3.6, reasoning disabled). AI_SEARCH_API_BASE_URL and
AI_SEARCH_MODEL are optional overrides.
Redeploy graph-api after setting secrets:
supabase functions deploy graph-apiFor local manual LinkedIn profile import testing without signing in, set a throwaway secret in local Vite and in the Edge Function environment:
# .env.local only, never production
VITE_LINKEDIN_ENRICHMENT_TEST_SECRET=dev-only-random-secret
# local Supabase Edge Function env, or a non-production test function deployment
LINKEDIN_ENRICHMENT_ALLOW_TEST_AUTH=true
LINKEDIN_ENRICHMENT_TEST_SECRET=dev-only-random-secretThe test bypass is accepted only when the request comes from localhost, 127.0.0.1,
or ::1, has the matching x-linkedin-enrichment-test-secret header, and the Edge
Function has LINKEDIN_ENRICHMENT_ALLOW_TEST_AUTH=true. Production must leave these
test variables unset so unauthenticated enrichment still fails.
Manual one-profile LinkedIn search imports call the profile enrichment Edge Function.
LinkedIn ZIP import parses the core archive locally, then signed-in users automatically
call enrich-linkedin-archive for server-side LLM context notes.
The browser never calls the LinkedIn enrichment provider directly. It invokes the Edge Function, and that function authenticates the user, calls the provider with a server-side API key, and returns normalized profile data.
Configure Supabase Auth redirect URLs for each app URL used by the team, including:
http://localhost:5173http://localhost:5174http://localhost:5175http://127.0.0.1:5173http://127.0.0.1:5174http://127.0.0.1:5175http://10.29.0.117:5173http://10.29.0.117:5174http://10.29.0.117:5175- the deployed production URL
The app redirects Google OAuth, email confirmation, and password recovery back to the
clean app origin/path without a hash fragment. Each origin used by the team must be
allowlisted in Supabase. Do not include #board in Supabase callback URLs; the client
navigates to #board after Supabase has restored a valid session. Login attempts include
the sdn_auth_return=board callback URL parameter and also store a short-lived board
return target in browser localStorage and sessionStorage. Supabase callback
parameters also render the board route while the session is restored, even if the provider
or allowlist handling drops the custom return parameter.
For a stable multi-device login flow, prefer one deployed frontend origin on your server instead of ad-hoc local ports.
If a teammate runs Vite on a different port such as 5173, 5174, or 5175, or opens the app through a LAN IP instead of localhost, that exact origin must be in the Supabase Auth URL configuration.
Google Cloud OAuth setup:
- Add the same localhost and
127.0.0.1origins to Authorized JavaScript origins. - Add the same LAN IP origins such as
http://10.29.0.117:5173when testing Google login from other devices on the network. - Keep the Supabase callback as the Authorized redirect URI:
https://lycfoukfoesobeuumuad.supabase.co/auth/v1/callback
The Supabase Google provider also needs the Google OAuth Client ID and Client Secret before Sign in with Google can complete.
If the Google OAuth app is still in Testing mode, add each teammate email to Test users in the Google Cloud OAuth consent screen.
Email/password auth setup:
- Enable email/password signups in Supabase Auth.
- Keep email confirmation enabled for production accounts.
- Customize the Supabase confirmation and recovery email templates in the Supabase Dashboard so they match Social Datanode's tone and link back to the allowlisted frontend origin.
- If password recovery links open the app but do not show the new-password dialog, verify that the recovery redirect URL exactly matches the frontend origin.
Use npm for this repository.
For a clean, reproducible install, use:
npm ciThis is the closest npm equivalent to a lockfile sync command. It installs exactly what is recorded in package-lock.json.
Use npm install only when intentionally adding, removing, or changing dependencies:
npm install <package>
npm install -D <package>To update installed packages within the semver ranges already declared in package.json, use:
npm updateTo inspect available updates before changing anything, use:
npm outdatedAfter any dependency change:
- Review
package.jsonandpackage-lock.json. - Run
npm run build. - Run
npm run lint. - Do not keep lockfile churn that is unrelated to the intended dependency change.
Create a production build:
npm run buildRun lint checks:
npm run lintPreview the production build locally:
npm run previewManual verification:
- Open the app in a browser.
- Drag anywhere on the board and confirm the point grid moves smoothly.
- On a phone or mobile emulator, drag with one finger anywhere on the board and confirm the point grid moves smoothly.
- Tap a person on a touch device and confirm the inspector opens instead of starting a stuck drag.
- Scroll on a trackpad and confirm the board pans without triggering zoom.
- Use the mouse wheel and confirm zoom centers around the cursor.
- Confirm the zoom indicator in the bottom-right updates smoothly.
- Open the sign-in dialog and confirm Google, email sign-in, create account, forgot password, and resend confirmation controls fit on desktop and mobile widths.
- Sign in with Google and confirm the account state appears in Settings.
- Create an account with only email and password, confirm the confirmation notice appears, and confirm resend confirmation does not clear the local board.
- Request a password reset and confirm the UI shows a generic success message.
- Open a Supabase password recovery link and confirm the app shows the new-password state.
- Confirm the signed-in account loads or creates a root
Youcircle. - Drag out from a node to create a new connected person and confirm it persists after reload.
- Drag a non-root person to a new position, confirm connected lines follow it, then reload and confirm the coordinates persist.
- Click a person near a viewport edge and confirm the board pans enough to keep the inspector visible.
- Confirm the inspector shows name, notes, connections, and delete actions for a person.
- Save a note with
Cmd/Ctrl + Enterand confirm it persists after reload. - Switch inspector selection and confirm unsaved note/connection drafts reset.
- Create a connection between two people, reload, then delete it with Backspace or the inspector.
- Open search and verify name/circle matches; click a result and confirm camera fly + inspector.
- Paste a LinkedIn profile URL into search while signed in and verify import/enrichment.
- Switch board tool modes (edit / select / pan) and confirm pan mode moves the canvas on one-finger drag (mobile or touch).
- Sign out and confirm anonymous editing still works with local persistence.
Supabase verification:
- Confirm one
user_graphsrow exists for the signed-in user after the first edit. - Confirm the
user_graphs.graphJSON containscircles,people, andconnections. - Confirm row-level security prevents reading or updating another user's
user_graphsrow.
- Use the Linear Hackathon project for all hackathon tasks.
- Keep task status, owner, priority, and blockers in Linear.
- Keep durable technical decisions and repo structure notes in
docs/. - Keep all repository text in English.
- Use
mainas the trunk branch. - Create short-lived task branches once implementation starts.
- Keep pull requests small and link them to the relevant Linear issue.
- Before starting new repo work, run
git fetchand thengit pull --ff-onlywhen the working tree is clean, so local work starts from the latest GitHub state without creating automatic merge commits. - Create a commit after repository changes.
- Do not push automatically after every change; push only when the user asks for it or the release workflow requires it.
- Commits on
maindo not deploy automatically; promote a reviewed ref with the production workflow when a release is ready.
See the Linear Hackathon project
for live task ownership and status. Durable technical direction lives in docs/.
- Keep the commands above current.
- Add test commands when automated tests are introduced.
- Note common failures and the next action to take.
- Add one obvious local verification command before each substantial pull request.
npm run buildnpm run lintnpm run test:load- Manual browser check of drag navigation, wheel and trackpad navigation, theme persistence, persisted graph editing, local search, and LinkedIn import
- Manual Supabase auth check when credentials and Google OAuth are configured