Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
14 changes: 14 additions & 0 deletions .claude/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"version": "0.0.1",
"configurations": [
{
"name": "storybook",
"runtimeExecutable": "bash",
"runtimeArgs": [
"-c",
"export PATH=\"$HOME/.nvm/versions/node/v22.21.1/bin:$PATH\"; exec npm run storybook"
],
"port": 6006

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== tracked launch.json =="
fd -a 'launch\.json$' .claude || true
if [ -f .claude/launch.json ]; then
  nl -ba .claude/launch.json | sed -n '1,120p'
fi

echo
echo "== node version declarations =="
for f in package.json .nvmrc .node-version; do
  if [ -f "$f" ]; then
    echo "--- $f ---"
    sed -n '1,160p' "$f"
  fi
done

echo
echo "== node-related scripts/config files =="
rg -n '"node"|"engines"|nvm|v22\.21\.1|`@nextcloud`' -S package.json .claude . (glob:!node_modules) || true

echo
echo "== Bash fallback semantics probe =="
tmpdir="$(mktemp -d)"
cat > "$tmpdir/make_path_scenario.sh" <<'BASH'
#!/usr/bin/env bash
HOME="$TMPDIR" PATH="$TMPDIR/bin:$TMPDIR/missing-subdir:$TMPDIR/lib:$PATH" sh -c 'export PATH="$HOME/.nvm/versions/node/v22.21.1/bin:$PATH"; type -a npm; [ -x "$(command -v npm 2>/dev/null || true)" ] && npm --help >/dev/null 2>&1'
BASH
TMPDIR="$tmpdir" bash "$tmpdir/make_path_scenario.sh"

Repository: alfredo1996/neoboard

Length of output: 263


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== .claude/launch.json =="
python3 - <<'PYTHON'
from pathlib import Path
p=Path('.claude/launch.json')
if p.exists():
    for i,line in enumerate(p.read_text().splitlines(),1):
        if 0 < i < 140:
            print(f"{i:4d}: {line}")
else:
    print("missing")
PYTHON

echo
echo "== node version declarations =="
for f in package.json .nvmrc .node-version; do
  if [ -f "$f" ]; then
    echo "--- $f ---"
    python3 -c "from pathlib import Path; print(Path('$f').read_text())"
  fi
  true
done

echo
echo "== node-related mentions =="
python3 - <<'PYTHON'
from pathlib import Path
for p in Path('.').rglob('*'):
    if p.is_file() and p not in (Path('.git'), Path('.github'), Path('node_modules'), Path('.claude')) and any(part in ('node_modules','.git') for part in p.parts):
        pass
    if p.is_file() and p != Path('.claude/launch.json'):
        txt = p.read_text(errors='ignore')
        if any(s in txt for s in ['"node"', 'nvm', 'v22.21.1', '.claude/launch.json']):
            print(f"\n--- {p} ---")
            for i,line in enumerate(txt.splitlines(),1):
                if any(s in line for s in ['"node"', 'nvm', 'v22.21.1']):
                    print(f"{i:4d}: {line}")
PYTHON

echo
echo "== Bash export fallback semantics probe =="
tmpdir="$(mktemp -ด)"
mkdir -p "$tmpdir/bin"
cat > "$tmpdir/bin/npm" <<'BASH'
#!/usr/bin/env bash
printf 'npm-version-fallback\n'
BASH
chmod +x "$tmpdir/bin/npm"

(
  HOME="$tmpdir" PATH="$tmpdir/bin" bash -c 'export PATH="$HOME/.nvm/versions/node/v22.21.1/bin:$PATH"; printf "npm: "; command -v npm; command -v npm >/dev/null && npm'
)

rm -rf "$tmpdir"

Repository: alfredo1996/neoboard

Length of output: 50377


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== .claude/launch.json =="
python3 - <<'PYTHON'
from pathlib import Path
p=Path('.claude/launch.json')
if p.exists():
    for i,line in enumerate(p.read_text().splitlines(),1):
        if 0 < i < 140:
            print(f"{i:4d}: {line}")
else:
    print("missing")
PYTHON

echo
echo "== node version declarations =="
for f in package.json .nvmrc .node-version; do
  if [ -f "$f" ]; then
    echo "--- $f ---"
    python3 -c "from pathlib import Path; print(Path('$f').read_text())"
  fi
  true
done

echo
echo "== node-related mentions =="
python3 - <<'PYTHON'
from pathlib import Path
for p in Path('.').rglob('*'):
    if p.is_file() and p != Path('.claude/launch.json') and any(part in ('node_modules','.git') for part in p.parts) == False:
        txt = p.read_text(errors='ignore')
        if any(s in txt for s in ['"node"', 'nvm', 'v22.21.1']):
            print(f"\n--- {p} ---")
            for i,line in enumerate(txt.splitlines(),1):
                if any(s in line for s in ['"node"', 'nvm', 'v22.21.1']):
                    print(f"{i:4d}: {line}")
PYTHON

echo
echo "== Bash export fallback semantics probe =="
tmpdir="$(mktemp -d)"
mkdir -p "$tmpdir/bin"
cat > "$tmpdir/bin/npm" <<'BASH'
#!/usr/bin/env bash
printf 'npm-version-fallback\n'
BASH
chmod +x "$tmpdir/bin/npm"

(
  HOME="$tmpdir" PATH="$tmpdir/bin" bash -c 'export PATH="$HOME/.nvm/versions/node/v22.21.1/bin:$PATH"; printf "npm: "; command -v npm; command -v npm >/dev/null && npm'
)

rm -rf "$tmpdir"

Repository: alfredo1996/neoboard

Length of output: 41181


Avoid hardcoding the Storybook Node version in .claude/launch.json.

NODE is currently 20, not 22.21.1, and the path is developer-specific. If that directory is missing, npm falls back to whatever is found next on PATH, so Storybook may start with the wrong node. Align this with the repo’s Node source or check a supported node version before exec npm run storybook.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/launch.json around lines 9 - 11, Update the Storybook launch command
in the launch configuration to remove the developer-specific hardcoded nvm Node
path and align Node selection with the repository’s supported Node
version/source. Ensure the command validates or selects a supported Node
executable before running npm run storybook, rather than silently falling back
to another PATH version.

}
]
}
4 changes: 2 additions & 2 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,9 +197,9 @@ app/src/
└── proxy.ts # Edge middleware (auth guard)

component/src/
├── charts/ # ECharts wrappers (BaseChart + 12 types)
├── charts/ # ECharts wrappers (BaseChart + 14 types)
├── components/
│ ├── ui/ # 33 shadcn/ui primitives
│ ├── ui/ # 38 shadcn/ui primitives
│ └── composed/ # 42 higher-order components
├── hooks/ # useWidgetSize, useContainerSize
└── lib/ # Utilities, design tokens, Cypher language
Expand Down
7 changes: 4 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Rules:

| Layer | Tool | Examples |
| -------------------- | ----------------------- | -------------------------------------------------------------------------------- |
| Pure functions/utils | Vitest (no DOM) | chart-registry, normalize-value, date-utils, query-hash, wrap-with-preview-limit |
| Pure functions/utils | Vitest (no DOM) | chart-plugin-registry, normalize-value, date-utils, query-hash, wrap-with-preview-limit |
| API routes | Vitest (mocked DB/auth) | Validation, permissions, error handling |
| Zustand stores | Vitest (no mocks) | State transitions, cascading logic |
| Store orchestration | Vitest (no DOM) | parameter-widget-renderer interactions, type coercion |
Expand Down Expand Up @@ -125,7 +125,8 @@ Playwright E2E with **server-side coverage collection** (`collectServer: true` i

## Multi-Tenancy

- `tenant_id` column on ALL tables. Every DB query MUST include tenant filter at ORM/middleware level.
- `tenant_id` column on ALL tables. Every DB query MUST include an explicit tenant filter — `eq(table.tenantId, session.tenantId)` — written **per query, in the route**. There is no ORM-level or middleware-level enforcement today (`lib/db/index.ts` is a plain Drizzle client), so a forgotten filter is a cross-tenant leak that nothing catches. Adding a guard is tracked in #1226.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
- Take `tenantId` from `requireSession()`, NEVER from the request body.
- JWT tokens include `tenantId` claim. Validate before ANY DB or API access.
- SaaS vs on-prem: env vars only, never code branches.

Expand All @@ -144,7 +145,7 @@ Includes: SSO, Custom Roles, Connector Labels, Bulk Import, Connector CRUD API,
## Migrations

Forward-only. Idempotent. Advisory lock prevents concurrent runs.
Test version-skip paths. `--skip-migrations` flag exists for emergency debugging.
Test version-skip paths. Boot migrations are controlled by `MIGRATE_ON_START` (`1`/`true` to run; set `0` to skip for emergency debugging) — there is no `--skip-migrations` CLI flag.

## Automated Guardrails (Hooks)

Expand Down
97 changes: 97 additions & 0 deletions app/src/lib/__tests__/docs-accuracy.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { describe, it, expect } from "vitest";
import { readFileSync, existsSync, readdirSync } from "node:fs";
import { resolve, dirname } from "node:path";
import { fileURLToPath } from "node:url";

/**
* Guards the repo's own documentation against drift (#1235).
*
* CLAUDE.md is loaded as ground truth by agent sessions, so a stale path or
* count there becomes a wrong assumption in generated code. These tests fail
* loudly instead.
*/

const REPO_ROOT = resolve(
dirname(fileURLToPath(import.meta.url)),
"../../../..",
);

const readDoc = (name: string) =>
readFileSync(resolve(REPO_ROOT, name), "utf8");

/** Top-level dirs that make a backticked token a repo path rather than an npm specifier. */
const REPO_PREFIXES = [
"app/",
"component/",
"connection/",
"connector-sdk/",
"cli/",
"docs/",
"scripts/",
".claude/",
".github/",
"docker/",
];

function referencedPaths(markdown: string): string[] {
const backticked = markdown.match(/`[^`\s]+`/g) ?? [];
return [
...new Set(
backticked
.map((t) => t.slice(1, -1))
.filter((t) => REPO_PREFIXES.some((p) => t.startsWith(p))),
),
];
}

const countFiles = (dir: string, ext = ".tsx") =>
readdirSync(resolve(REPO_ROOT, dir)).filter((f) => f.endsWith(ext)).length;

describe("documentation accuracy", () => {
describe.each(["CLAUDE.md", "ARCHITECTURE.md"])("%s", (docName) => {
it("references only file paths that exist", () => {
const missing = referencedPaths(readDoc(docName)).filter(
(p) => !existsSync(resolve(REPO_ROOT, p)),
);
expect(missing).toEqual([]);
});
});

describe("ARCHITECTURE.md component counts match the filesystem", () => {
// Each regex must match: a reworded claim should fail here rather than
// silently stop being checked.
it.each([
[
"shadcn/ui primitives",
/(\d+) shadcn\/ui primitives/,
() => countFiles("component/src/components/ui"),
],
[
"composed components",
/(\d+) higher-order components/,
() => countFiles("component/src/components/composed"),
],
[
"chart modules",
/BaseChart \+ (\d+) types/,
// charts/ holds base-chart.tsx plus one module per chart type.
() => countFiles("component/src/charts") - 1,
],
])("%s", (_label, pattern, actual) => {
const match = readDoc("ARCHITECTURE.md").match(pattern);
expect(
match,
`claim matching ${pattern} not found — was it reworded?`,
).not.toBeNull();
expect(Number(match![1])).toBe(actual());
});
});

it("CLAUDE.md documents MIGRATE_ON_START, not a --skip-migrations flag", () => {
// Naming the flag to debunk it is fine (readers search for it); asserting
// it exists is not. The real escape hatch is MIGRATE_ON_START=0 (#1222).
const doc = readDoc("CLAUDE.md");
expect(doc).toContain("MIGRATE_ON_START");
expect(doc).not.toMatch(/`--skip-migrations`\s+flag\s+exists/);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
});
});
Loading