@elevasis/sdk 1.6.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +5 -5
- package/package.json +2 -2
- package/reference/claude-config/hooks/__tests__/pre-edit-vibe-gate.test.mjs +169 -0
- package/reference/claude-config/hooks/pre-edit-vibe-gate.mjs +128 -0
- package/reference/claude-config/logs/pre-edit-vibe-gate.log +23 -0
- package/reference/claude-config/rules/organization-os.md +55 -8
- package/reference/claude-config/rules/vibe.md +210 -0
- package/reference/claude-config/settings.json +11 -0
- package/reference/claude-config/skills/configure/SKILL.md +100 -0
- package/reference/claude-config/skills/configure/operations/codify-level-a.md +100 -0
- package/reference/claude-config/skills/configure/operations/codify-level-b.md +158 -0
- package/reference/claude-config/skills/configure/operations/customers.md +150 -0
- package/reference/claude-config/skills/configure/operations/features.md +163 -0
- package/reference/claude-config/skills/configure/operations/goals.md +147 -0
- package/reference/claude-config/skills/configure/operations/identity.md +133 -0
- package/reference/claude-config/skills/configure/operations/labels.md +128 -0
- package/reference/claude-config/skills/configure/operations/offerings.md +159 -0
- package/reference/claude-config/skills/configure/operations/roles.md +153 -0
- package/reference/claude-config/skills/configure/operations/techStack.md +139 -0
- package/reference/claude-config/skills/setup/SKILL.md +81 -32
- package/reference/packages/core/src/organization-model/README.md +16 -12
- package/reference/scaffold/core/organization-graph.mdx +1 -0
- package/reference/scaffold/core/organization-model.mdx +84 -19
- package/reference/scaffold/recipes/add-a-feature.md +1 -1
- package/reference/scaffold/recipes/customize-organization-model.md +5 -5
- package/reference/scaffold/recipes/gate-by-feature-or-admin.md +3 -3
- package/reference/scaffold/reference/contracts.md +115 -7
- package/reference/scaffold/reference/glossary.md +25 -4
package/dist/cli.cjs
CHANGED
|
@@ -44004,7 +44004,7 @@ function wrapAction(commandName, fn) {
|
|
|
44004
44004
|
// package.json
|
|
44005
44005
|
var package_default = {
|
|
44006
44006
|
name: "@elevasis/sdk",
|
|
44007
|
-
version: "1.
|
|
44007
|
+
version: "1.7.0",
|
|
44008
44008
|
description: "SDK for building Elevasis organization resources",
|
|
44009
44009
|
type: "module",
|
|
44010
44010
|
bin: {
|
|
@@ -45494,9 +45494,7 @@ function renderProjectWorkBrief(brief) {
|
|
|
45494
45494
|
if (!brief.resumeTarget) {
|
|
45495
45495
|
lines.push(" No clear resume target yet.");
|
|
45496
45496
|
} else {
|
|
45497
|
-
lines.push(
|
|
45498
|
-
` ${brief.resumeTarget.kind}: ${brief.resumeTarget.name} (${brief.resumeTarget.status ?? "unknown"})`
|
|
45499
|
-
);
|
|
45497
|
+
lines.push(` ${brief.resumeTarget.kind}: ${brief.resumeTarget.name} (${brief.resumeTarget.status ?? "unknown"})`);
|
|
45500
45498
|
if (brief.resumeTarget.savedState) {
|
|
45501
45499
|
lines.push(` Saved state: ${brief.resumeTarget.savedState}`);
|
|
45502
45500
|
}
|
|
@@ -45544,7 +45542,9 @@ Projects (${projects.length}):
|
|
|
45544
45542
|
);
|
|
45545
45543
|
}
|
|
45546
45544
|
function registerProjectResolve(program3) {
|
|
45547
|
-
program3.command("project:resolve <query>").description(
|
|
45545
|
+
program3.command("project:resolve <query>").description(
|
|
45546
|
+
'Resolve a project ID from a name, UUID, or search query\n Example: elevasis-sdk project:resolve "Alpha"'
|
|
45547
|
+
).option("--api-url <url>", "API base URL").option("--pretty", "Render project details instead of only the resolved ID").action(
|
|
45548
45548
|
wrapAction("project:resolve", async (query, options2) => {
|
|
45549
45549
|
const project = await resolveProject(query, options2.apiUrl);
|
|
45550
45550
|
if (options2.pretty) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elevasis/sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "SDK for building Elevasis organization resources",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"tsup": "^8.0.0",
|
|
45
45
|
"typescript": "5.9.2",
|
|
46
46
|
"zod": "^4.1.0",
|
|
47
|
-
"@repo/core": "0.
|
|
47
|
+
"@repo/core": "0.6.0",
|
|
48
48
|
"@repo/typescript-config": "0.0.0"
|
|
49
49
|
},
|
|
50
50
|
"scripts": {
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// pre-edit-vibe-gate.test.mjs
|
|
3
|
+
// Tests for the pre-edit-vibe-gate hook.
|
|
4
|
+
// Run with: node --test .claude/hooks/__tests__/pre-edit-vibe-gate.test.mjs
|
|
5
|
+
//
|
|
6
|
+
// Tests the exported `isProtectedPath` helper directly (pure logic, no stdin/stdout
|
|
7
|
+
// mocking needed). Integration cases simulate the full hook by spawning it as a
|
|
8
|
+
// child process.
|
|
9
|
+
|
|
10
|
+
import { describe, it } from 'node:test'
|
|
11
|
+
import assert from 'node:assert/strict'
|
|
12
|
+
import { spawnSync } from 'node:child_process'
|
|
13
|
+
import { resolve, dirname } from 'node:path'
|
|
14
|
+
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
15
|
+
|
|
16
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
17
|
+
const HOOK_PATH = resolve(__dirname, '..', 'pre-edit-vibe-gate.mjs')
|
|
18
|
+
|
|
19
|
+
// Import the exported helper for unit-level tests.
|
|
20
|
+
// Using a dynamic import so the module's top-level await (stdin read) doesn't
|
|
21
|
+
// execute — the hook guards that behind the stdin loop which only runs when
|
|
22
|
+
// there is actual input; unit tests call the exported function directly.
|
|
23
|
+
// On Windows, absolute paths must be converted to file:// URLs for ESM imports.
|
|
24
|
+
const { isProtectedPath } = await import(pathToFileURL(HOOK_PATH).href)
|
|
25
|
+
|
|
26
|
+
// Template root = 3 levels up from __tests__/
|
|
27
|
+
const TEMPLATE_ROOT = resolve(__dirname, '..', '..', '..')
|
|
28
|
+
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// Helper: run the hook as a subprocess with a synthetic PreToolUse event.
|
|
31
|
+
// filePath may be relative (to template root) or already absolute.
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
function runHook(toolName, filePath, env = {}) {
|
|
34
|
+
// Claude always sends absolute paths to hooks. Resolve relative paths
|
|
35
|
+
// against the template root so toRelative() inside the hook works correctly.
|
|
36
|
+
const absFilePath =
|
|
37
|
+
filePath.startsWith('/') || /^[A-Za-z]:/.test(filePath) ? filePath : resolve(TEMPLATE_ROOT, filePath)
|
|
38
|
+
|
|
39
|
+
const event = JSON.stringify({
|
|
40
|
+
tool_name: toolName,
|
|
41
|
+
tool_input: { file_path: absFilePath }
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
return spawnSync(process.execPath, [HOOK_PATH], {
|
|
45
|
+
input: event,
|
|
46
|
+
encoding: 'utf-8',
|
|
47
|
+
env: {
|
|
48
|
+
...process.env,
|
|
49
|
+
// Override AFTER spreading so this always wins over any ambient value
|
|
50
|
+
CLAUDE_PROJECT_DIR: TEMPLATE_ROOT,
|
|
51
|
+
...env
|
|
52
|
+
},
|
|
53
|
+
timeout: 5000
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
// Unit tests — isProtectedPath (pure function, no process spawning)
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
|
|
61
|
+
describe('isProtectedPath — exact protected files', () => {
|
|
62
|
+
it('blocks organization-model.ts', () => {
|
|
63
|
+
assert.equal(isProtectedPath('foundations/config/organization-model.ts'), true)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it('blocks organization-model.examples.ts', () => {
|
|
67
|
+
assert.equal(isProtectedPath('foundations/config/organization-model.examples.ts'), true)
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('blocks organization-model.override.ts', () => {
|
|
71
|
+
assert.equal(isProtectedPath('foundations/config/organization-model.override.ts'), true)
|
|
72
|
+
})
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
describe('isProtectedPath — extensions glob', () => {
|
|
76
|
+
it('blocks a .ts file directly under extensions/', () => {
|
|
77
|
+
assert.equal(isProtectedPath('foundations/config/extensions/deal-ecom.ts'), true)
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('blocks a .ts file in a subdirectory under extensions/', () => {
|
|
81
|
+
assert.equal(isProtectedPath('foundations/config/extensions/crm/deal-ecom.ts'), true)
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('blocks the discriminated-union index.ts under extensions/', () => {
|
|
85
|
+
assert.equal(isProtectedPath('foundations/config/extensions/index.ts'), true)
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it('does NOT block a non-.ts file under extensions/', () => {
|
|
89
|
+
assert.equal(isProtectedPath('foundations/config/extensions/README.md'), false)
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
describe('isProtectedPath — unrelated paths always pass', () => {
|
|
94
|
+
it('allows ui/ files', () => {
|
|
95
|
+
assert.equal(isProtectedPath('ui/src/routes/__root.tsx'), false)
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('allows operations/ files', () => {
|
|
99
|
+
assert.equal(isProtectedPath('operations/src/index.ts'), false)
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it('allows foundations/types (not config)', () => {
|
|
103
|
+
assert.equal(isProtectedPath('foundations/types/entities.ts'), false)
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it('does NOT block a partial-match path that is not under foundations/config', () => {
|
|
107
|
+
// "organization-model.ts" without the full prefix should not be blocked
|
|
108
|
+
assert.equal(isProtectedPath('src/organization-model.ts'), false)
|
|
109
|
+
})
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
describe('isProtectedPath — partial path / edge cases', () => {
|
|
113
|
+
it('does not block foundations/config/ itself (no .ts suffix)', () => {
|
|
114
|
+
// The directory path should not be blocked
|
|
115
|
+
assert.equal(isProtectedPath('foundations/config/'), false)
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
it('strips leading ./ before matching', () => {
|
|
119
|
+
assert.equal(isProtectedPath('./foundations/config/organization-model.ts'), true)
|
|
120
|
+
})
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
// Integration tests — spawn the hook with synthetic events
|
|
125
|
+
// ---------------------------------------------------------------------------
|
|
126
|
+
|
|
127
|
+
describe('hook subprocess — blocked without VIBE_APPROVED', () => {
|
|
128
|
+
it('exits 2 for Write to organization-model.ts', () => {
|
|
129
|
+
const result = runHook('Write', 'foundations/config/organization-model.ts')
|
|
130
|
+
assert.equal(result.status, 2, `expected exit 2, got ${result.status}`)
|
|
131
|
+
assert.ok(result.stderr.includes('BLOCKED'), 'expected BLOCKED in stderr')
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
it('exits 2 for Edit to extensions/deal-ecom.ts', () => {
|
|
135
|
+
const result = runHook('Edit', 'foundations/config/extensions/deal-ecom.ts')
|
|
136
|
+
assert.equal(result.status, 2)
|
|
137
|
+
assert.ok(result.stderr.includes('BLOCKED'))
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
it('exits 2 for MultiEdit to organization-model.override.ts', () => {
|
|
141
|
+
const result = runHook('MultiEdit', 'foundations/config/organization-model.override.ts')
|
|
142
|
+
assert.equal(result.status, 2)
|
|
143
|
+
assert.ok(result.stderr.includes('BLOCKED'))
|
|
144
|
+
})
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
describe('hook subprocess — passes with VIBE_APPROVED=1', () => {
|
|
148
|
+
it('exits 0 for Write to organization-model.ts when VIBE_APPROVED=1', () => {
|
|
149
|
+
const result = runHook('Write', 'foundations/config/organization-model.ts', { VIBE_APPROVED: '1' })
|
|
150
|
+
assert.equal(result.status, 0, `expected exit 0, got ${result.status}`)
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
it('exits 0 for Edit to extensions/deal-ecom.ts when VIBE_APPROVED=1', () => {
|
|
154
|
+
const result = runHook('Edit', 'foundations/config/extensions/deal-ecom.ts', { VIBE_APPROVED: '1' })
|
|
155
|
+
assert.equal(result.status, 0)
|
|
156
|
+
})
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
describe('hook subprocess — unrelated path always passes', () => {
|
|
160
|
+
it('exits 0 for Write to ui/src/routes/__root.tsx without VIBE_APPROVED', () => {
|
|
161
|
+
const result = runHook('Write', 'ui/src/routes/__root.tsx')
|
|
162
|
+
assert.equal(result.status, 0)
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
it('exits 0 for Edit to operations/src/index.ts without VIBE_APPROVED', () => {
|
|
166
|
+
const result = runHook('Edit', 'operations/src/index.ts')
|
|
167
|
+
assert.equal(result.status, 0)
|
|
168
|
+
})
|
|
169
|
+
})
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// pre-edit-vibe-gate.mjs
|
|
3
|
+
// PreToolUse backstop — blocks raw writes to protected foundations/config paths
|
|
4
|
+
// unless the VIBE_APPROVED=1 trust marker is present.
|
|
5
|
+
//
|
|
6
|
+
// Protected paths (relative to project root):
|
|
7
|
+
// foundations/config/organization-model.ts
|
|
8
|
+
// foundations/config/organization-model.examples.ts
|
|
9
|
+
// foundations/config/organization-model.override.ts
|
|
10
|
+
// foundations/config/extensions/**/*.ts
|
|
11
|
+
//
|
|
12
|
+
// To allow a write: set VIBE_APPROVED=1 in the environment before invoking the
|
|
13
|
+
// agent, or run /configure which sets the marker automatically after user
|
|
14
|
+
// confirmation.
|
|
15
|
+
|
|
16
|
+
import { appendFileSync, mkdirSync } from 'node:fs'
|
|
17
|
+
import { normalize, resolve, relative, sep } from 'node:path'
|
|
18
|
+
import { fileURLToPath } from 'node:url'
|
|
19
|
+
|
|
20
|
+
const ROOT = normalize(process.env.CLAUDE_PROJECT_DIR ?? process.cwd())
|
|
21
|
+
const LOG_DIR = ROOT + sep + '.claude' + sep + 'logs'
|
|
22
|
+
const LOG_FILE = LOG_DIR + sep + 'pre-edit-vibe-gate.log'
|
|
23
|
+
|
|
24
|
+
function log(msg) {
|
|
25
|
+
try {
|
|
26
|
+
mkdirSync(LOG_DIR, { recursive: true })
|
|
27
|
+
appendFileSync(LOG_FILE, `[${new Date().toISOString()}] ${msg}\n`)
|
|
28
|
+
} catch {
|
|
29
|
+
// Never crash on log failure
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Normalise an incoming file path (may be absolute or project-relative) to a
|
|
35
|
+
* forward-slash relative path from ROOT for consistent matching.
|
|
36
|
+
*/
|
|
37
|
+
function toRelative(filePath) {
|
|
38
|
+
const abs = normalize(resolve(filePath))
|
|
39
|
+
// relative() returns OS-separator paths — normalise to forward slashes
|
|
40
|
+
return relative(ROOT, abs).replace(/\\/g, '/')
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Returns true if `rel` (forward-slash relative path from root) matches one of
|
|
45
|
+
* the protected path patterns. No external deps — uses simple string checks.
|
|
46
|
+
*
|
|
47
|
+
* Protected patterns:
|
|
48
|
+
* foundations/config/organization-model.ts (exact)
|
|
49
|
+
* foundations/config/organization-model.examples.ts (exact)
|
|
50
|
+
* foundations/config/organization-model.override.ts (exact)
|
|
51
|
+
* foundations/config/extensions/**\/*.ts (glob — any .ts under extensions/)
|
|
52
|
+
*/
|
|
53
|
+
export function isProtectedPath(rel) {
|
|
54
|
+
// Normalise away any leading ./
|
|
55
|
+
const r = rel.replace(/^\.\//, '')
|
|
56
|
+
|
|
57
|
+
// Exact protected files
|
|
58
|
+
const EXACT = [
|
|
59
|
+
'foundations/config/organization-model.ts',
|
|
60
|
+
'foundations/config/organization-model.examples.ts',
|
|
61
|
+
'foundations/config/organization-model.override.ts'
|
|
62
|
+
]
|
|
63
|
+
if (EXACT.includes(r)) return true
|
|
64
|
+
|
|
65
|
+
// Glob: foundations/config/extensions/**/*.ts
|
|
66
|
+
// Must start with the prefix, end with .ts, and have at least one path segment
|
|
67
|
+
// after the extensions/ directory.
|
|
68
|
+
const EXT_PREFIX = 'foundations/config/extensions/'
|
|
69
|
+
if (r.startsWith(EXT_PREFIX) && r.endsWith('.ts')) {
|
|
70
|
+
// Ensure there is at least one filename after the prefix (not just the dir itself)
|
|
71
|
+
const remainder = r.slice(EXT_PREFIX.length)
|
|
72
|
+
if (remainder.length > 3) return true // at least "x.ts"
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return false
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const BLOCK_MESSAGE =
|
|
79
|
+
'BLOCKED: This file is protected by the vibe gate.\n' +
|
|
80
|
+
'WHY: Direct edits to foundations/config/organization-model.ts and\n' +
|
|
81
|
+
' foundations/config/extensions/*.ts bypass the ambient vibe ceremony\n' +
|
|
82
|
+
' (confirm + /configure write + pnpm check-types).\n' +
|
|
83
|
+
'FIX: Run /configure to codify changes with proper ceremony (sets VIBE_APPROVED=1\n' +
|
|
84
|
+
' automatically), or set VIBE_APPROVED=1 explicitly if you are a power user\n' +
|
|
85
|
+
' who has already completed the ceremony manually.'
|
|
86
|
+
|
|
87
|
+
// Only run the stdin-reading main loop when this file is the entry point,
|
|
88
|
+
// not when it is imported as a module (e.g., by tests).
|
|
89
|
+
const isMain =
|
|
90
|
+
process.argv[1] != null && normalize(fileURLToPath(import.meta.url)) === normalize(resolve(process.argv[1]))
|
|
91
|
+
|
|
92
|
+
if (isMain) {
|
|
93
|
+
try {
|
|
94
|
+
const chunks = []
|
|
95
|
+
for await (const chunk of process.stdin) chunks.push(chunk)
|
|
96
|
+
const input = JSON.parse(Buffer.concat(chunks).toString())
|
|
97
|
+
|
|
98
|
+
const toolName = input.tool_name ?? ''
|
|
99
|
+
const filePath = input.tool_input?.file_path ?? ''
|
|
100
|
+
|
|
101
|
+
// Only gate Write, Edit, MultiEdit
|
|
102
|
+
if (!['Write', 'Edit', 'MultiEdit'].includes(toolName)) {
|
|
103
|
+
log(`SKIP tool=${toolName} (not a write tool)`)
|
|
104
|
+
process.exit(0)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (!filePath) {
|
|
108
|
+
log(`SKIP tool=${toolName} no file_path in input`)
|
|
109
|
+
process.exit(0)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const rel = toRelative(filePath)
|
|
113
|
+
const protected_ = isProtectedPath(rel)
|
|
114
|
+
const approved = process.env.VIBE_APPROVED === '1'
|
|
115
|
+
const decision = protected_ && !approved ? 'BLOCK' : 'ALLOW'
|
|
116
|
+
|
|
117
|
+
log(`tool=${toolName} path=${rel} protected=${protected_} VIBE_APPROVED=${approved} decision=${decision}`)
|
|
118
|
+
|
|
119
|
+
if (decision === 'BLOCK') {
|
|
120
|
+
process.stderr.write(BLOCK_MESSAGE)
|
|
121
|
+
process.exit(2)
|
|
122
|
+
}
|
|
123
|
+
} catch (err) {
|
|
124
|
+
log(`ERROR: ${err?.message ?? String(err)}`)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
process.exit(0)
|
|
128
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
[2026-04-20T09:29:53.658Z] tool=Write path=../../foundations/config/organization-model.ts protected=false VIBE_APPROVED=false decision=ALLOW
|
|
2
|
+
[2026-04-20T09:29:53.702Z] tool=Edit path=../../foundations/config/extensions/deal-ecom.ts protected=false VIBE_APPROVED=false decision=ALLOW
|
|
3
|
+
[2026-04-20T09:29:53.747Z] tool=MultiEdit path=../../foundations/config/organization-model.override.ts protected=false VIBE_APPROVED=false decision=ALLOW
|
|
4
|
+
[2026-04-20T09:29:53.793Z] tool=Write path=../../foundations/config/organization-model.ts protected=false VIBE_APPROVED=true decision=ALLOW
|
|
5
|
+
[2026-04-20T09:29:53.837Z] tool=Edit path=../../foundations/config/extensions/deal-ecom.ts protected=false VIBE_APPROVED=true decision=ALLOW
|
|
6
|
+
[2026-04-20T09:29:53.890Z] tool=Write path=../../ui/src/routes/__root.tsx protected=false VIBE_APPROVED=false decision=ALLOW
|
|
7
|
+
[2026-04-20T09:29:53.934Z] tool=Edit path=../../operations/src/index.ts protected=false VIBE_APPROVED=false decision=ALLOW
|
|
8
|
+
[2026-04-20T09:32:06.844Z] tool=Write path=../../foundations/config/organization-model.ts protected=false VIBE_APPROVED=false decision=ALLOW
|
|
9
|
+
[2026-04-20T09:32:06.890Z] tool=Edit path=../../foundations/config/extensions/deal-ecom.ts protected=false VIBE_APPROVED=false decision=ALLOW
|
|
10
|
+
[2026-04-20T09:32:06.934Z] tool=MultiEdit path=../../foundations/config/organization-model.override.ts protected=false VIBE_APPROVED=false decision=ALLOW
|
|
11
|
+
[2026-04-20T09:32:06.978Z] tool=Write path=../../foundations/config/organization-model.ts protected=false VIBE_APPROVED=true decision=ALLOW
|
|
12
|
+
[2026-04-20T09:32:07.021Z] tool=Edit path=../../foundations/config/extensions/deal-ecom.ts protected=false VIBE_APPROVED=true decision=ALLOW
|
|
13
|
+
[2026-04-20T09:32:07.065Z] tool=Write path=../../ui/src/routes/__root.tsx protected=false VIBE_APPROVED=false decision=ALLOW
|
|
14
|
+
[2026-04-20T09:32:07.109Z] tool=Edit path=../../operations/src/index.ts protected=false VIBE_APPROVED=false decision=ALLOW
|
|
15
|
+
[2026-04-20T09:32:30.340Z] tool=Write path=../../foundations/config/organization-model.ts protected=false VIBE_APPROVED=false decision=ALLOW
|
|
16
|
+
[2026-04-20T09:38:14.624Z] tool=Write path=../../foundations/config/organization-model.ts protected=false VIBE_APPROVED=false decision=ALLOW
|
|
17
|
+
[2026-04-20T09:40:20.586Z] tool=Write path=foundations/config/organization-model.ts protected=true VIBE_APPROVED=false decision=BLOCK
|
|
18
|
+
[2026-04-20T09:40:20.631Z] tool=Edit path=foundations/config/extensions/deal-ecom.ts protected=true VIBE_APPROVED=false decision=BLOCK
|
|
19
|
+
[2026-04-20T09:40:20.674Z] tool=MultiEdit path=foundations/config/organization-model.override.ts protected=true VIBE_APPROVED=false decision=BLOCK
|
|
20
|
+
[2026-04-20T09:40:20.718Z] tool=Write path=foundations/config/organization-model.ts protected=true VIBE_APPROVED=true decision=ALLOW
|
|
21
|
+
[2026-04-20T09:40:20.762Z] tool=Edit path=foundations/config/extensions/deal-ecom.ts protected=true VIBE_APPROVED=true decision=ALLOW
|
|
22
|
+
[2026-04-20T09:40:20.807Z] tool=Write path=ui/src/routes/__root.tsx protected=false VIBE_APPROVED=false decision=ALLOW
|
|
23
|
+
[2026-04-20T09:40:20.905Z] tool=Edit path=operations/src/index.ts protected=false VIBE_APPROVED=false decision=ALLOW
|
|
@@ -5,12 +5,34 @@ Organization OS is the semantic contract layer defining how organizations, featu
|
|
|
5
5
|
## Key Files in This Project
|
|
6
6
|
|
|
7
7
|
- `foundations/config/organization-model.ts` -- project-specific org model definition (calls `defineOrganizationModel` + `resolveOrganizationModel`)
|
|
8
|
-
- `foundations/config/organization-model.examples.ts` --
|
|
8
|
+
- `foundations/config/organization-model.examples.ts` -- reference patterns for all 14 domains: branding, identity, customers, offerings, roles, goals, sales stages, prospecting stages, resource mappings with techStack, custom features, statuses, and open-placement navigation groups. Pure reference -- not imported anywhere. Read this when customizing the org model.
|
|
9
9
|
- `foundations/types/entities.ts` -- typed entity contracts (Project, Deal, etc.). Extends `BaseProject`, `BaseDeal` from `@elevasis/core/entities` with project-specific metadata. Read this when authoring workflows that operate on these entities.
|
|
10
10
|
- `ui/src/routes/__root.tsx` -- wires `ElevasisFeaturesProvider` with `canonicalOrganizationModel`
|
|
11
11
|
- `ui/src/app-config.ts` -- references the org model
|
|
12
12
|
- `operations/src/index.ts` -- `DeploymentSpec` registry for workflows and agents
|
|
13
13
|
|
|
14
|
+
## Domain Overview
|
|
15
|
+
|
|
16
|
+
As of the 2026-04-20 expansion, `OrganizationModel` contains 14 top-level domains:
|
|
17
|
+
|
|
18
|
+
**Platform configuration:** `features`, `branding`, `navigation`, `sales` (formerly `crm`), `prospecting` (formerly `leadGen`), `projects` (formerly `delivery`), `resourceMappings`
|
|
19
|
+
|
|
20
|
+
**Organizational reality:** `identity`, `customers`, `offerings`, `roles`, `goals`
|
|
21
|
+
|
|
22
|
+
**Vibe layer:** `statuses`, `operations`
|
|
23
|
+
|
|
24
|
+
The `resourceMappings` entries may carry an optional `techStack` extension (`platform`, `purpose`, `credentialStatus`, `isSystemOfRecord`) without introducing a new top-level domain.
|
|
25
|
+
|
|
26
|
+
### Domain Rename Note
|
|
27
|
+
|
|
28
|
+
Three field names changed in the 2026-04-20 expansion. Feature ID constants and consumer-facing feature IDs are unchanged:
|
|
29
|
+
|
|
30
|
+
| Old field | New field | Feature ID (unchanged) | Constant (unchanged) |
|
|
31
|
+
| ---------- | ------------- | ---------------------- | --------------------- |
|
|
32
|
+
| `crm` | `sales` | `'crm'` | `CRM_FEATURE_ID` |
|
|
33
|
+
| `leadGen` | `prospecting` | `'lead-gen'` | `LEAD_GEN_FEATURE_ID` |
|
|
34
|
+
| `delivery` | `projects` | `'projects'` | `PROJECTS_FEATURE_ID` |
|
|
35
|
+
|
|
14
36
|
## Reference Documentation
|
|
15
37
|
|
|
16
38
|
Full Organization OS documentation ships with the SDK and is available locally after `pnpm install`:
|
|
@@ -20,7 +42,7 @@ Full Organization OS documentation ships with the SDK and is available locally a
|
|
|
20
42
|
All paths under `node_modules/@elevasis/sdk/reference/scaffold/`:
|
|
21
43
|
|
|
22
44
|
- `node_modules/@elevasis/sdk/reference/scaffold/index.mdx` -- scaffold root and navigation
|
|
23
|
-
- `node_modules/@elevasis/sdk/reference/scaffold/core/organization-model.mdx` -- semantic contract,
|
|
45
|
+
- `node_modules/@elevasis/sdk/reference/scaffold/core/organization-model.mdx` -- semantic contract, all 14 domains, adapter authoring, validation gate, `/configure` entry point
|
|
24
46
|
- `node_modules/@elevasis/sdk/reference/scaffold/core/organization-graph.mdx` -- graph derivation, node/edge taxonomy, lenses
|
|
25
47
|
- `node_modules/@elevasis/sdk/reference/scaffold/ui/feature-shell.mdx` -- FeatureModule manifest, provider runtime
|
|
26
48
|
- `node_modules/@elevasis/sdk/reference/scaffold/ui/composition-extensibility.mdx` -- layout primitives, router abstraction
|
|
@@ -48,15 +70,40 @@ All paths under `node_modules/@elevasis/sdk/reference/scaffold/`:
|
|
|
48
70
|
- `@elevasis/core/organization-model` -- the curated organization-model barrel. Exports `defineOrganizationModel`, `resolveOrganizationModel`, `OrganizationModelSchema`, `DEFAULT_ORGANIZATION_MODEL`, organization-model types, and typed feature/surface constants.
|
|
49
71
|
- Feature IDs: `CRM_FEATURE_ID`, `LEAD_GEN_FEATURE_ID`, `PROJECTS_FEATURE_ID`, `OPERATIONS_FEATURE_ID`, `MONITORING_FEATURE_ID`, `SETTINGS_FEATURE_ID`, `SEO_FEATURE_ID`
|
|
50
72
|
- Headline surface IDs: `CRM_PIPELINE_SURFACE_ID`, `LEAD_GEN_LISTS_SURFACE_ID`, `PROJECTS_INDEX_SURFACE_ID`, `OPERATIONS_ORGANIZATION_GRAPH_SURFACE_ID`
|
|
51
|
-
-
|
|
73
|
+
- Reality domain types: `OrganizationModelIdentity`, `OrganizationModelCustomers`, `OrganizationModelCustomerSegment`, `OrganizationModelOfferings`, `OrganizationModelProduct`, `OrganizationModelRoles`, `OrganizationModelRole`, `OrganizationModelGoals`, `OrganizationModelObjective`, `OrganizationModelKeyResult`
|
|
74
|
+
- Vibe domain types: `OrganizationModelStatuses`, `OrganizationModelOperations`
|
|
75
|
+
- TechStack: `TechStackEntrySchema`, `OrganizationModelTechStackEntry`
|
|
76
|
+
- Use constants instead of magic strings when overriding the org model.
|
|
52
77
|
- `@elevasis/core/entities` -- entity contracts barrel. Exports `BaseProject`, `BaseProjectSchema`, `BaseProjectInput` and the equivalents for `Milestone`, `Task`, `Deal`, `Company`, `Contact`. Each base interface is generic over a `\<TMeta>` extension slot. Extend these in `foundations/types/entities.ts` to add project-specific fields.
|
|
53
78
|
|
|
54
79
|
## When Working with Organization OS
|
|
55
80
|
|
|
56
|
-
- **
|
|
57
|
-
- **Adding a
|
|
58
|
-
- **
|
|
81
|
+
- **Changing org model (structural reality):** Use `/configure` as the entry point. Do not edit `foundations/config/organization-model.ts` directly -- the `pre-edit-vibe-gate.mjs` hook blocks raw writes without `VIBE_APPROVED=1`, which `/configure` sets after user confirmation. Run `/configure` for the full layered flow or `/configure \<domain>` for a targeted domain.
|
|
82
|
+
- **Adding a feature:** Follow `node_modules/@elevasis/sdk/reference/scaffold/recipes/add-a-feature.md`. For toggling an existing feature, use `/configure features`.
|
|
83
|
+
- **Adding a resource:** Follow `node_modules/@elevasis/sdk/reference/scaffold/recipes/add-a-resource.md`.
|
|
59
84
|
- **Extending entities:** Start with `foundations/types/entities.ts` for the demo extension pattern. Base shapes come from `@elevasis/core/entities`.
|
|
60
85
|
- **Authoring a workflow that takes a Project/Deal/etc.:** Reference entity types from `foundations/types/entities.ts` in the input schema -- do not redeclare them.
|
|
61
|
-
- **Understanding contracts:** Check `node_modules/@elevasis/sdk/reference/scaffold/reference/contracts.md` for current type shapes
|
|
62
|
-
- **Debugging sync issues:** Check `node_modules/@elevasis/sdk/reference/scaffold/operations/propagation-pipeline.md` for the verification pipeline
|
|
86
|
+
- **Understanding contracts:** Check `node_modules/@elevasis/sdk/reference/scaffold/reference/contracts.md` for current type shapes.
|
|
87
|
+
- **Debugging sync issues:** Check `node_modules/@elevasis/sdk/reference/scaffold/operations/propagation-pipeline.md` for the verification pipeline.
|
|
88
|
+
|
|
89
|
+
## `/configure` -- Org Model QA Entry Point
|
|
90
|
+
|
|
91
|
+
`/configure` is the recurring, safe-to-re-run org model editor for this project. It is a skill (not a command) at `.claude/skills/configure/SKILL.md`.
|
|
92
|
+
|
|
93
|
+
**Usage:**
|
|
94
|
+
|
|
95
|
+
- `/configure` -- layered flow: identity → customers → offerings → roles → goals → techStack
|
|
96
|
+
- `/configure identity` -- legal identity, mission/vision, industry, geography, timezone
|
|
97
|
+
- `/configure customers` -- customer segments with jobs-to-be-done, pains, gains, firmographics
|
|
98
|
+
- `/configure offerings` -- products and services with pricing model and segment references
|
|
99
|
+
- `/configure roles` -- role chart with responsibilities, reporting lines, and holders
|
|
100
|
+
- `/configure goals` -- organizational goals with period and measurable outcomes
|
|
101
|
+
- `/configure techStack` -- external-SaaS integration metadata on resource mappings
|
|
102
|
+
- `/configure features` -- enable, disable, or add features
|
|
103
|
+
- `/configure labels` -- edit display labels on enum entries (statuses, stages)
|
|
104
|
+
|
|
105
|
+
Every write is gated: `resolveOrganizationModel()` must succeed (Zod cross-refs pass) and `pnpm -C operations check-types` must pass. On failure the change is rolled back.
|
|
106
|
+
|
|
107
|
+
**Distinction from `/setup`:** `/setup` is first-time bootstrap only. After bootstrap it delegates here. `/configure` is idempotent and safe to re-run at any time.
|
|
108
|
+
|
|
109
|
+
The ambient vibe layer (`.claude/rules/vibe.md`) automatically detects Codify intent in plain language and delegates to `/configure`. Power users can invoke `/configure` directly to bypass the ambient layer entirely.
|