Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
cd13134
feat: build/release pipeline — separate dev from distribution (#20) (…
davekilleen Mar 6, 2026
aec4826
feat: Phase 4 — release automation, fixture vault, rollback manifest …
davekilleen Mar 6, 2026
056b123
feat(testing): enforce lane-a merge gates and CI quality policies
davekilleen Mar 8, 2026
9c09ba7
fix(ci): use valid GitHub branch protection payload
davekilleen Mar 8, 2026
0e09382
feat(testing): expand lane-b behavior tests and hook harness gates
davekilleen Mar 8, 2026
eca7278
Lane C: add migration safety, security/perf gates, and nightly quality
davekilleen Mar 8, 2026
46692c0
docs: add testing hardening stack merge runbook
davekilleen Mar 8, 2026
1808f41
fix(ci): handle checklist grep patterns starting with dash
davekilleen Mar 8, 2026
3c01d26
fix(ci): handle checklist grep patterns starting with dash
davekilleen Mar 8, 2026
63b6e1e
fix(ci): handle checklist grep patterns starting with dash
davekilleen Mar 8, 2026
42ab034
test(ci): run screenpipe integration test without pytest-asyncio
davekilleen Mar 8, 2026
cfbda0c
fix(ci): allowlist path-contract script self-reference
davekilleen Mar 8, 2026
6a57f2b
test(ci): run screenpipe integration test without pytest-asyncio
davekilleen Mar 8, 2026
46e8899
test(ci): run screenpipe integration test without pytest-asyncio
davekilleen Mar 8, 2026
c164431
test(ci): skip screenpipe integration when aiohttp is unavailable
davekilleen Mar 8, 2026
7c74216
test(ci): skip screenpipe integration when aiohttp is unavailable
davekilleen Mar 8, 2026
3742aad
fix(ci): remove raw PARA literals from lane-c path gate targets
davekilleen Mar 8, 2026
2e40bce
test(ci): skip screenpipe integration when aiohttp is unavailable
davekilleen Mar 8, 2026
d60d015
Merge pull request #25 from davekilleen/lane-a-merge-gates
davekilleen Mar 8, 2026
92d730c
Merge pull request #28 from davekilleen/lane-b-test-depth
davekilleen Mar 8, 2026
2a93424
Merge pull request #29 from davekilleen/lane-c-platform-hardening
davekilleen Mar 8, 2026
a613830
feat: ship Ritual Intelligence v1
davekilleen Mar 10, 2026
30a4e6e
test: stabilize ritual intelligence fixtures
davekilleen Mar 10, 2026
803a690
chore: satisfy ritual intelligence quality checks
davekilleen Mar 10, 2026
48ef577
chore: retrigger governance checks
davekilleen Mar 10, 2026
74a5158
fix: make Ralph Wiggum Loop section optional in PR governance check
davekilleen Mar 10, 2026
0822430
fix: satisfy ritual intelligence path contracts
davekilleen Mar 10, 2026
e57d319
test: cover ritual intelligence entrypoints
davekilleen Mar 10, 2026
e2c43d6
test: stub eventkit import in CI
davekilleen Mar 10, 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
25 changes: 16 additions & 9 deletions .claude/hooks/company-context-injector.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,34 @@
const fs = require('fs');
const path = require('path');

const DEBUG_SKIP = process.env.DEX_HOOK_DEBUG === '1';
function skip(reason) {
if (DEBUG_SKIP) {
console.error(`[dex-hook-skip] ${reason}`);
}
process.exit(0);
}

// Read hook input from stdin
let input;
try {
input = JSON.parse(fs.readFileSync(0, 'utf-8'));
} catch (e) {
process.exit(0); // Invalid input, skip silently
skip('invalid-json-input');
}

const filePath = input.tool_input?.path || input.tool_input?.file_path || '';

// Skip if no file path or if reading a company page itself (avoid recursion)
if (!filePath || filePath.includes('/05-Areas/Companies/') || filePath.includes('/05-Areas/Accounts/')) {
process.exit(0);
skip('missing-file-path-or-recursive-company-file');
}

// Skip binary/non-text files (images, PDFs, archives, etc.)
const ext = path.extname(filePath).toLowerCase();
const skipExts = ['.png', '.jpg', '.jpeg', '.gif', '.bmp', '.webp', '.ico', '.svg', '.pdf', '.zip', '.tar', '.gz', '.mp3', '.mp4', '.mov', '.wav', '.pptx', '.xlsx', '.docx'];
if (skipExts.includes(ext)) {
process.exit(0);
skip(`unsupported-extension:${ext}`);
}

const { loadPaths } = require('./paths.cjs');
Expand Down Expand Up @@ -101,7 +109,7 @@ try {
const fullFilePath = filePath.startsWith('/') ? filePath : path.join(VAULT_ROOT, filePath);

if (!fs.existsSync(fullFilePath)) {
process.exit(0);
skip(`target-file-not-found:${fullFilePath}`);
}

// Read the file to find company references
Expand All @@ -112,7 +120,7 @@ try {
const companyNames = Object.keys(companyIndex);

if (companyNames.length === 0) {
process.exit(0);
skip('no-company-pages-indexed');
}

// Find referenced companies in the content
Expand Down Expand Up @@ -151,7 +159,7 @@ try {

// Only proceed if we found companies
if (foundCompanies.size === 0) {
process.exit(0);
skip('no-company-references-found');
}

// Look up each company and build context
Expand All @@ -166,7 +174,7 @@ try {

// Only inject if we found relevant company pages
if (companyContexts.length === 0) {
process.exit(0);
skip('company-context-parse-empty');
}

// Build context (silent - no headers, just data)
Expand Down Expand Up @@ -211,8 +219,7 @@ try {
console.log(JSON.stringify(output));

} catch (e) {
// Silently fail - don't block the read operation
process.exit(0);
skip(`unexpected-error:${e.message}`);
}

/**
Expand Down
25 changes: 16 additions & 9 deletions .claude/hooks/person-context-injector.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,34 @@
const fs = require('fs');
const path = require('path');

const DEBUG_SKIP = process.env.DEX_HOOK_DEBUG === '1';
function skip(reason) {
if (DEBUG_SKIP) {
console.error(`[dex-hook-skip] ${reason}`);
}
process.exit(0);
}

// Read hook input from stdin
let input;
try {
input = JSON.parse(fs.readFileSync(0, 'utf-8'));
} catch (e) {
process.exit(0); // Invalid input, skip silently
skip('invalid-json-input');
}

const filePath = input.tool_input?.path || input.tool_input?.file_path || '';

// Skip if no file path or if reading a Person page itself (avoid recursion)
if (!filePath || filePath.includes('/People/')) {
process.exit(0);
skip('missing-file-path-or-recursive-person-file');
}

// Skip binary/non-text files (images, PDFs, archives, etc.)
const ext = path.extname(filePath).toLowerCase();
const skipExts = ['.png', '.jpg', '.jpeg', '.gif', '.bmp', '.webp', '.ico', '.svg', '.pdf', '.zip', '.tar', '.gz', '.mp3', '.mp4', '.mov', '.wav', '.pptx', '.xlsx', '.docx'];
if (skipExts.includes(ext)) {
process.exit(0);
skip(`unsupported-extension:${ext}`);
}

const { loadPaths } = require('./paths.cjs');
Expand Down Expand Up @@ -86,7 +94,7 @@ try {
const fullFilePath = filePath.startsWith('/') ? filePath : path.join(VAULT_ROOT, filePath);

if (!fs.existsSync(fullFilePath)) {
process.exit(0);
skip(`target-file-not-found:${fullFilePath}`);
}

// Read the file to find person references
Expand All @@ -97,7 +105,7 @@ try {
const personNames = Object.keys(personIndex);

if (personNames.length === 0) {
process.exit(0);
skip('no-person-pages-indexed');
}

// Find referenced people in the content
Expand Down Expand Up @@ -135,7 +143,7 @@ try {

// Only proceed if we found people
if (foundPeople.size === 0) {
process.exit(0);
skip('no-person-references-found');
}

// Look up each person and build context
Expand All @@ -150,7 +158,7 @@ try {

// Only inject if we found relevant person pages
if (personContexts.length === 0) {
process.exit(0);
skip('person-context-parse-empty');
}

// Build context (silent - no headers, just data)
Expand Down Expand Up @@ -185,8 +193,7 @@ try {
console.log(JSON.stringify(output));

} catch (e) {
// Silently fail - don't block the read operation
process.exit(0);
skip(`unexpected-error:${e.message}`);
}

/**
Expand Down
37 changes: 37 additions & 0 deletions .claude/hooks/tests/context-injectors.test.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
const test = require('node:test');
const assert = require('node:assert/strict');
const { spawnSync } = require('node:child_process');
const path = require('node:path');

function runHook(scriptName, stdin) {
const scriptPath = path.join(__dirname, '..', scriptName);
return spawnSync('node', [scriptPath], {
input: stdin,
encoding: 'utf-8',
env: { ...process.env, DEX_HOOK_DEBUG: '1' },
});
}

test('person context injector emits skip reason on invalid JSON', () => {
const result = runHook('person-context-injector.cjs', 'not-json');
assert.equal(result.status, 0);
assert.match(result.stderr, /\[dex-hook-skip] invalid-json-input/);
});

test('person context injector emits skip reason when file path missing', () => {
const result = runHook('person-context-injector.cjs', JSON.stringify({ tool_input: {} }));
assert.equal(result.status, 0);
assert.match(result.stderr, /\[dex-hook-skip] missing-file-path-or-recursive-person-file/);
});

test('company context injector emits skip reason on invalid JSON', () => {
const result = runHook('company-context-injector.cjs', '{oops');
assert.equal(result.status, 0);
assert.match(result.stderr, /\[dex-hook-skip] invalid-json-input/);
});

test('company context injector emits skip reason when file path missing', () => {
const result = runHook('company-context-injector.cjs', JSON.stringify({ tool_input: {} }));
assert.equal(result.status, 0);
assert.match(result.stderr, /\[dex-hook-skip] missing-file-path-or-recursive-company-file/);
});
38 changes: 36 additions & 2 deletions .claude/skills/dex-rollback/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,35 @@ This restores all Dex files to the state before update.

### Step 5: Cleanup

**A. Remove dependencies from newer version**
**A. Remove files added by the newer version (manifest-based)**

If `System/.installed-files.manifest` exists for the **current** (newer) version,
use it to detect files that were added by the update and should be removed:

```bash
# Save manifests before reset
cp System/.installed-files.manifest /tmp/dex-new-manifest.txt 2>/dev/null || true
```

After `git reset --hard` in Step 4, compare:

```bash
if [ -f /tmp/dex-new-manifest.txt ] && [ -f System/.installed-files.manifest ]; then
# Files in new manifest but NOT in restored manifest = added by update
comm -23 \
<(awk '{print $NF}' /tmp/dex-new-manifest.txt | sort) \
<(awk '{print $NF}' System/.installed-files.manifest | sort) \
| while read -r f; do
[ -f "$f" ] && rm "$f" && echo " Removed: $f"
done
echo "✓ Cleaned up files added by the update"
else
echo "ℹ️ No manifest found — skipping file cleanup (safe to ignore)"
fi
rm -f /tmp/dex-new-manifest.txt
```

**B. Reinstall dependencies for the restored version**

```
📦 Cleaning up...
Expand All @@ -150,7 +178,13 @@ pip3 install -r core/mcp/requirements.txt

This ensures dependencies match the older version.

**B. Remove migration markers (if exist)**
**C. Regenerate manifest for the restored version**

```bash
bash scripts/generate-manifest.sh
```

**D. Remove migration markers (if exist)**

```bash
rm -f .migration-v*-complete
Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/dex-update/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ If cancelled:

Run:
```bash
git merge upstream/main --no-edit
git merge upstream/release --no-edit
```

**B. Handle merge outcome**
Expand Down
29 changes: 29 additions & 0 deletions .distignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Dev-only files excluded from user distribution
# Used by scripts/build-release.sh and .gitattributes export-ignore

# CI / GitHub workflows
.github/

# Dev tooling configs
pyproject.toml
vitest.config.mjs
.husky/

# Test suites
core/tests/
.claude/hooks/__tests__/
.scripts/meeting-intel/__tests__/

# Dev scripts and docs
CONTRIBUTING.md
DISTRIBUTION_READY.md
scripts/

# TypeScript dev config
pi-extensions/dex/tsconfig.json

# Issue tracking (worktree artifacts)
.ao-issue.md

# This file itself
.distignore
17 changes: 17 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Exclude dev-only files from git archive (ZIP downloads)
# Mirrors .distignore — keep both in sync

.github/ export-ignore
pyproject.toml export-ignore
vitest.config.mjs export-ignore
.husky/ export-ignore
core/tests/ export-ignore
.claude/hooks/__tests__/ export-ignore
.scripts/meeting-intel/__tests__/ export-ignore
CONTRIBUTING.md export-ignore
DISTRIBUTION_READY.md export-ignore
scripts/ export-ignore
pi-extensions/dex/tsconfig.json export-ignore
.ao-issue.md export-ignore
.distignore export-ignore
.gitattributes export-ignore
9 changes: 9 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Default owner for repository changes.
* @davekilleen

# High-risk areas require explicit review ownership.
.github/ @davekilleen
core/mcp/ @davekilleen
scripts/ @davekilleen
System/PRDs/ @davekilleen
docs/ @davekilleen
31 changes: 31 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
## Linked Issue
- Linear issue: `DEX-XX`

## What Changed
-

## Test Plan
- Unit/integration tests added or updated:
- Negative/error-path tests added or updated:
- Commands run locally:

## Ralph Wiggum Loop
- [ ] I implemented the change.
- [ ] I self-reviewed for defects and edge cases.
- [ ] I requested specialist review for risky areas (testing/infra/security when relevant).
- [ ] I addressed review findings and re-ran checks.

## Quality Gates
- [ ] I added/updated tests or documented why no tests are needed.
- [ ] I added a regression test for bug fixes, or this PR is not a bug fix.
- [ ] I validated failure modes / edge cases.
- [ ] I updated docs or confirmed no docs impact.
- [ ] CI checks for lint + tests + coverage are expected to pass.

## Risk & Rollback
- Risk level (low/medium/high):
- Rollback plan:

## Docs Impact
- Files updated:
- If none, reason:
Loading
Loading