Planning artifacts (milestones v1.0-v1.2.1, v1.3 queue, PROJECT.md, STATE.md, config) now live alongside the code they describe. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
346 lines
11 KiB
Markdown
346 lines
11 KiB
Markdown
---
|
|
phase: 01-foundation
|
|
plan: 01
|
|
type: execute
|
|
wave: 1
|
|
depends_on: []
|
|
files_modified:
|
|
- packages/branding/package.json
|
|
- packages/branding/tsconfig.json
|
|
- packages/branding/src/index.ts
|
|
- packages/branding/src/vocab.ts
|
|
- packages/branding/src/vocab.test.ts
|
|
- packages/branding/vitest.config.ts
|
|
- vitest.config.ts
|
|
autonomous: true
|
|
requirements: [FOUND-01]
|
|
|
|
must_haves:
|
|
truths:
|
|
- "import { VOCAB } from '@paperclipai/branding' resolves and returns an object with all required vocabulary keys"
|
|
- "VOCAB.company equals 'Workspace', VOCAB.ceo equals 'Project Manager', VOCAB.appName equals 'Nexus'"
|
|
- "Unit tests pass confirming every VOCAB key has the correct string value"
|
|
artifacts:
|
|
- path: "packages/branding/package.json"
|
|
provides: "Workspace package definition"
|
|
contains: "@paperclipai/branding"
|
|
- path: "packages/branding/src/vocab.ts"
|
|
provides: "VOCAB constant with all display strings"
|
|
exports: ["VOCAB", "VocabKey"]
|
|
- path: "packages/branding/src/index.ts"
|
|
provides: "Package barrel export"
|
|
exports: ["VOCAB", "VocabKey"]
|
|
- path: "packages/branding/src/vocab.test.ts"
|
|
provides: "Unit tests for VOCAB shape and values"
|
|
min_lines: 20
|
|
key_links:
|
|
- from: "packages/branding/src/index.ts"
|
|
to: "packages/branding/src/vocab.ts"
|
|
via: "re-export"
|
|
pattern: "export.*from.*vocab"
|
|
- from: "vitest.config.ts"
|
|
to: "packages/branding"
|
|
via: "projects array entry"
|
|
pattern: "packages/branding"
|
|
---
|
|
|
|
<objective>
|
|
Create the `packages/branding/` workspace package that centralizes all Nexus fork-specific display strings in a single `VOCAB` constant. This is the string mutation surface that all downstream phases (2, 3, 4) will import from.
|
|
|
|
Purpose: Isolate all Nexus vocabulary from upstream Paperclip code so that rebase operations never conflict on display strings.
|
|
Output: A working, tested `@paperclipai/branding` package importable by any workspace member.
|
|
</objective>
|
|
|
|
<execution_context>
|
|
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
|
|
@$HOME/.claude/get-shit-done/templates/summary.md
|
|
</execution_context>
|
|
|
|
<context>
|
|
@.planning/PROJECT.md
|
|
@.planning/ROADMAP.md
|
|
@.planning/STATE.md
|
|
@.planning/phases/01-foundation/01-RESEARCH.md
|
|
</context>
|
|
|
|
<interfaces>
|
|
<!-- Reference package pattern from packages/shared/ — executor should replicate this structure -->
|
|
|
|
From packages/shared/package.json:
|
|
```json
|
|
{
|
|
"name": "@paperclipai/shared",
|
|
"version": "0.3.1",
|
|
"license": "MIT",
|
|
"type": "module",
|
|
"exports": {
|
|
".": "./src/index.ts",
|
|
"./*": "./src/*.ts"
|
|
},
|
|
"publishConfig": {
|
|
"access": "public",
|
|
"exports": {
|
|
".": {
|
|
"types": "./dist/index.d.ts",
|
|
"import": "./dist/index.js"
|
|
},
|
|
"./*": {
|
|
"types": "./dist/*.d.ts",
|
|
"import": "./dist/*.js"
|
|
}
|
|
},
|
|
"main": "./dist/index.js",
|
|
"types": "./dist/index.d.ts"
|
|
},
|
|
"files": ["dist"],
|
|
"scripts": {
|
|
"build": "tsc",
|
|
"clean": "rm -rf dist",
|
|
"typecheck": "tsc --noEmit"
|
|
}
|
|
}
|
|
```
|
|
|
|
From packages/shared/tsconfig.json:
|
|
```json
|
|
{
|
|
"extends": "../../tsconfig.base.json",
|
|
"compilerOptions": {
|
|
"outDir": "dist",
|
|
"rootDir": "src"
|
|
},
|
|
"include": ["src"]
|
|
}
|
|
```
|
|
|
|
From vitest.config.ts (root):
|
|
```typescript
|
|
import { defineConfig } from "vitest/config";
|
|
export default defineConfig({
|
|
test: {
|
|
projects: ["packages/db", "packages/adapters/opencode-local", "server", "ui", "cli"],
|
|
},
|
|
});
|
|
```
|
|
|
|
From pnpm-workspace.yaml (confirms packages/* glob):
|
|
```yaml
|
|
packages:
|
|
- packages/*
|
|
- packages/adapters/*
|
|
- packages/plugins/*
|
|
- packages/plugins/examples/*
|
|
- server
|
|
- ui
|
|
- cli
|
|
```
|
|
</interfaces>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto" tdd="true">
|
|
<name>Task 1: Scaffold branding package and write VOCAB constant with tests</name>
|
|
<files>
|
|
packages/branding/package.json,
|
|
packages/branding/tsconfig.json,
|
|
packages/branding/src/vocab.ts,
|
|
packages/branding/src/vocab.test.ts,
|
|
packages/branding/src/index.ts,
|
|
packages/branding/vitest.config.ts
|
|
</files>
|
|
<read_first>
|
|
/Volumes/UsbNvme/repos/nexus/packages/shared/package.json,
|
|
/Volumes/UsbNvme/repos/nexus/packages/shared/tsconfig.json,
|
|
/Volumes/UsbNvme/repos/nexus/packages/shared/src/index.ts,
|
|
/Volumes/UsbNvme/repos/nexus/pnpm-workspace.yaml
|
|
</read_first>
|
|
<behavior>
|
|
- Test: VOCAB has key "company" with value "Workspace"
|
|
- Test: VOCAB has key "companies" with value "Workspaces"
|
|
- Test: VOCAB has key "ceo" with value "Project Manager"
|
|
- Test: VOCAB has key "board" with value "Owner"
|
|
- Test: VOCAB has key "hire" with value "Add"
|
|
- Test: VOCAB has key "fire" with value "Remove"
|
|
- Test: VOCAB has key "appName" with value "Nexus"
|
|
- Test: VOCAB has key "tagline" with value "Open-source orchestration for your agents"
|
|
- Test: VocabKey type is exported (TypeScript compilation succeeds)
|
|
- Test: All VOCAB values are non-empty strings
|
|
</behavior>
|
|
<action>
|
|
1. Create `packages/branding/package.json` following the `packages/shared/package.json` pattern exactly:
|
|
- `"name": "@paperclipai/branding"` (keep @paperclipai scope per upstream sync constraint)
|
|
- `"version": "0.1.0"`
|
|
- `"license": "MIT"`
|
|
- `"type": "module"`
|
|
- `"exports": { ".": "./src/index.ts", "./*": "./src/*.ts" }`
|
|
- `"publishConfig"` with dist paths matching shared pattern
|
|
- `"files": ["dist"]`
|
|
- `"scripts": { "build": "tsc", "clean": "rm -rf dist", "typecheck": "tsc --noEmit" }`
|
|
- `"devDependencies": { "typescript": "^5.7.3" }` — no runtime dependencies
|
|
|
|
2. Create `packages/branding/tsconfig.json`:
|
|
```json
|
|
{
|
|
"extends": "../../tsconfig.base.json",
|
|
"compilerOptions": {
|
|
"outDir": "dist",
|
|
"rootDir": "src"
|
|
},
|
|
"include": ["src"]
|
|
}
|
|
```
|
|
|
|
3. Create `packages/branding/vitest.config.ts`:
|
|
```typescript
|
|
import { defineConfig } from "vitest/config";
|
|
export default defineConfig({
|
|
test: {
|
|
include: ["src/**/*.test.ts"],
|
|
},
|
|
});
|
|
```
|
|
|
|
4. Create `packages/branding/src/vocab.ts` with the VOCAB constant:
|
|
```typescript
|
|
export const VOCAB = {
|
|
// Entity renames (display only — code identifiers unchanged)
|
|
company: "Workspace",
|
|
companies: "Workspaces",
|
|
ceo: "Project Manager",
|
|
board: "Owner",
|
|
hire: "Add",
|
|
fire: "Remove",
|
|
|
|
// Brand name
|
|
appName: "Nexus",
|
|
tagline: "Open-source orchestration for your agents",
|
|
} as const;
|
|
|
|
export type VocabKey = keyof typeof VOCAB;
|
|
```
|
|
|
|
5. Create `packages/branding/src/index.ts`:
|
|
```typescript
|
|
export { VOCAB, type VocabKey } from "./vocab.js";
|
|
```
|
|
|
|
6. Create `packages/branding/src/vocab.test.ts` — RED first (write tests before verifying they pass):
|
|
```typescript
|
|
import { describe, it, expect } from "vitest";
|
|
import { VOCAB } from "./vocab.js";
|
|
|
|
describe("VOCAB", () => {
|
|
it("maps company to Workspace", () => {
|
|
expect(VOCAB.company).toBe("Workspace");
|
|
});
|
|
it("maps companies to Workspaces", () => {
|
|
expect(VOCAB.companies).toBe("Workspaces");
|
|
});
|
|
it("maps ceo to Project Manager", () => {
|
|
expect(VOCAB.ceo).toBe("Project Manager");
|
|
});
|
|
it("maps board to Owner", () => {
|
|
expect(VOCAB.board).toBe("Owner");
|
|
});
|
|
it("maps hire to Add", () => {
|
|
expect(VOCAB.hire).toBe("Add");
|
|
});
|
|
it("maps fire to Remove", () => {
|
|
expect(VOCAB.fire).toBe("Remove");
|
|
});
|
|
it("has appName as Nexus", () => {
|
|
expect(VOCAB.appName).toBe("Nexus");
|
|
});
|
|
it("has a non-empty tagline", () => {
|
|
expect(VOCAB.tagline).toBe("Open-source orchestration for your agents");
|
|
});
|
|
it("all values are non-empty strings", () => {
|
|
for (const [key, value] of Object.entries(VOCAB)) {
|
|
expect(typeof value).toBe("string");
|
|
expect(value.length).toBeGreaterThan(0);
|
|
}
|
|
});
|
|
});
|
|
```
|
|
|
|
7. Run `pnpm install` from repo root to link the new workspace package.
|
|
|
|
8. Run tests to confirm GREEN: `pnpm vitest run --project packages/branding`
|
|
</action>
|
|
<verify>
|
|
<automated>cd /Volumes/UsbNvme/repos/nexus && pnpm vitest run --project packages/branding</automated>
|
|
</verify>
|
|
<acceptance_criteria>
|
|
- packages/branding/package.json contains `"name": "@paperclipai/branding"`
|
|
- packages/branding/package.json contains `"type": "module"`
|
|
- packages/branding/package.json contains `"exports"`
|
|
- packages/branding/src/vocab.ts contains `export const VOCAB`
|
|
- packages/branding/src/vocab.ts contains `as const`
|
|
- packages/branding/src/vocab.ts contains `company: "Workspace"`
|
|
- packages/branding/src/vocab.ts contains `ceo: "Project Manager"`
|
|
- packages/branding/src/vocab.ts contains `appName: "Nexus"`
|
|
- packages/branding/src/vocab.ts contains `export type VocabKey`
|
|
- packages/branding/src/index.ts contains `export { VOCAB`
|
|
- packages/branding/src/vocab.test.ts contains `describe("VOCAB"`
|
|
- packages/branding/tsconfig.json contains `extends": "../../tsconfig.base.json"`
|
|
- `pnpm vitest run --project packages/branding` exits 0 with all tests passing
|
|
</acceptance_criteria>
|
|
<done>
|
|
All 9 VOCAB tests pass. Package exports VOCAB and VocabKey. Package is linked in pnpm workspace.
|
|
</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>Task 2: Register branding package in root vitest config</name>
|
|
<files>vitest.config.ts</files>
|
|
<read_first>
|
|
/Volumes/UsbNvme/repos/nexus/vitest.config.ts
|
|
</read_first>
|
|
<action>
|
|
Edit `vitest.config.ts` at the repo root to add `"packages/branding"` to the `projects` array:
|
|
|
|
```typescript
|
|
import { defineConfig } from "vitest/config";
|
|
|
|
export default defineConfig({
|
|
test: {
|
|
projects: ["packages/db", "packages/adapters/opencode-local", "server", "ui", "cli", "packages/branding"],
|
|
},
|
|
});
|
|
```
|
|
|
|
The only change is appending `"packages/branding"` to the end of the existing `projects` array.
|
|
|
|
After editing, run `pnpm vitest run --project packages/branding` to confirm the root config picks up the new project.
|
|
</action>
|
|
<verify>
|
|
<automated>cd /Volumes/UsbNvme/repos/nexus && pnpm vitest run --project packages/branding</automated>
|
|
</verify>
|
|
<acceptance_criteria>
|
|
- vitest.config.ts contains `"packages/branding"` in the projects array
|
|
- `pnpm vitest run --project packages/branding` exits 0
|
|
</acceptance_criteria>
|
|
<done>
|
|
Root vitest config includes branding package. Running `pnpm vitest run --project packages/branding` from root succeeds.
|
|
</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<verification>
|
|
1. `pnpm vitest run --project packages/branding` — all VOCAB tests pass
|
|
2. `node -e "const b = await import('./packages/branding/src/index.ts'); console.log(b.VOCAB.appName)"` — prints "Nexus" (requires tsx or similar TS runner)
|
|
3. `test -f packages/branding/package.json && echo OK` — package.json exists
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
- `@paperclipai/branding` package exists at `packages/branding/`
|
|
- `VOCAB` constant exports 8 keys: company, companies, ceo, board, hire, fire, appName, tagline
|
|
- All values are correct Nexus display strings
|
|
- Unit tests pass via vitest
|
|
- Package is registered in root vitest config
|
|
</success_criteria>
|
|
|
|
<output>
|
|
After completion, create `.planning/phases/01-foundation/01-01-SUMMARY.md`
|
|
</output>
|