fix template, remove okr, use casan.*
This commit is contained in:
@@ -16,6 +16,8 @@ export interface GoalProject {
|
||||
domain: string;
|
||||
domain_root: string;
|
||||
context_roots: string[];
|
||||
manifest?: string;
|
||||
shell_root?: string;
|
||||
}
|
||||
|
||||
export interface GoalProjectCreateInput {
|
||||
@@ -109,6 +111,7 @@ const ORCHESTRATOR_CLI = join(HARNESS_BIN, 'goal-orchestrator.py');
|
||||
const PATCH_EXECUTOR_CLI = join(HARNESS_BIN, 'goal-patch-executor.py');
|
||||
const RBAC_CLI = join(HARNESS_BIN, 'rbac-check.py');
|
||||
const PROJECT_REGISTRY = join(APP_ROOT, 'packages', 'casan-harness', 'level5', 'project-registry.json');
|
||||
const PROJECT_SCAFFOLDER = join(APP_ROOT, 'packages', 'casan-devkit', 'project-scaffold.py');
|
||||
|
||||
function parseJson<T>(value: string): T | null {
|
||||
try {
|
||||
@@ -260,10 +263,10 @@ export class GoalsService {
|
||||
if (actor.role !== 'org-admin') throw new ForbiddenException('GOAL_PROJECT_CREATE_DENIED');
|
||||
const projectId = String(input.projectId ?? '').trim();
|
||||
const domain = String(input.domain ?? '').trim();
|
||||
if (!/^[A-Za-z][A-Za-z0-9._-]{2,63}$/.test(projectId)) {
|
||||
if (!/^[a-z][a-z0-9-]{1,62}$/.test(projectId)) {
|
||||
throw new BadRequestException('GOAL_PROJECT_ID_INVALID');
|
||||
}
|
||||
if (domain.length < 3 || domain.length > 100) {
|
||||
if (!/^[\p{L}\p{N}][\p{L}\p{N} .&()'_-]{1,99}$/u.test(domain)) {
|
||||
throw new BadRequestException('GOAL_PROJECT_DOMAIN_INVALID');
|
||||
}
|
||||
const lockPath = `${PROJECT_REGISTRY}.lock`;
|
||||
@@ -291,13 +294,83 @@ export class GoalsService {
|
||||
absoluteRoot = join(canonicalProjects, projectId);
|
||||
mkdirSync(absoluteRoot, { mode: 0o750 });
|
||||
createdRoot = true;
|
||||
const relativeRoot = join('apps', 'projects', projectId);
|
||||
const entry = { project_id: projectId, domain, domain_root: relativeRoot, context_roots: [relativeRoot], harness_package: 'fpt-casan-sdd-harness', harness_version: '1.0.0', status: 'active' };
|
||||
execFileSync('python3', [PROJECT_SCAFFOLDER,
|
||||
'--target', absoluteRoot,
|
||||
'--project', projectId,
|
||||
'--name', domain,
|
||||
'--template', 'nestjs-react',
|
||||
'--with-harness',
|
||||
], {
|
||||
cwd: APP_ROOT,
|
||||
env: process.env,
|
||||
encoding: 'utf8',
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
timeout: 60_000,
|
||||
});
|
||||
|
||||
const relativeRoot = join('apps', 'projects', projectId).replaceAll('\\', '/');
|
||||
const embeddedAppRoot = `${relativeRoot}/apps/${projectId}`;
|
||||
const adapterManifestPath = `${relativeRoot}/casan.workspace.manifest.json`;
|
||||
const adapterManifest = {
|
||||
schema_version: 1,
|
||||
project_id: projectId,
|
||||
display_name: domain,
|
||||
domain_root: `${embeddedAppRoot}/domain`,
|
||||
requirements: `${embeddedAppRoot}/domain/input/requirement.md`,
|
||||
architecture: `${embeddedAppRoot}/domain/input/architecture.md`,
|
||||
quality_profile: `${relativeRoot}/config/casan/quality-profiles/enterprise-web-v1.json`,
|
||||
feature: { id: `001-${projectId}-app`, module_id: 'MOD-01', slug: `${projectId}-core`, title: `${domain} Core` },
|
||||
source_roots: [`${embeddedAppRoot}/backend`, `${embeddedAppRoot}/frontend`],
|
||||
commands: {
|
||||
build: [['npm', '--prefix', relativeRoot, 'run', 'build']],
|
||||
test: [['npm', '--prefix', relativeRoot, 'test']],
|
||||
},
|
||||
verification: [
|
||||
{
|
||||
path_prefix: `${embeddedAppRoot}/backend/`,
|
||||
commands: [
|
||||
['npm', '--prefix', relativeRoot, 'run', 'build', '-w', `@${projectId}/backend`],
|
||||
['npm', '--prefix', relativeRoot, 'test', '-w', `@${projectId}/backend`],
|
||||
],
|
||||
},
|
||||
{
|
||||
path_prefix: `${embeddedAppRoot}/frontend/`,
|
||||
commands: [
|
||||
['npm', '--prefix', relativeRoot, 'run', 'build', '-w', `@${projectId}/frontend`],
|
||||
['npm', '--prefix', relativeRoot, 'test', '-w', `@${projectId}/frontend`],
|
||||
],
|
||||
},
|
||||
],
|
||||
artifacts_root: `${relativeRoot}/docs/output`,
|
||||
implementation_evidence: [
|
||||
`${embeddedAppRoot}/backend/src/main.ts`,
|
||||
`${embeddedAppRoot}/backend/test/health.test.ts`,
|
||||
`${embeddedAppRoot}/frontend/src/App.tsx`,
|
||||
`${embeddedAppRoot}/frontend/src/__tests__/App.test.tsx`,
|
||||
],
|
||||
tech_stack: 'NestJS 10, React 18, Vite 5, Tailwind CSS 3, strict TypeScript',
|
||||
};
|
||||
const adapterAbsolute = join(APP_ROOT, adapterManifestPath);
|
||||
const adapterTemporary = `${adapterAbsolute}.${process.pid}.${randomUUID()}.tmp`;
|
||||
writeFileSync(adapterTemporary, `${JSON.stringify(adapterManifest, null, 2)}\n`, { encoding: 'utf8', mode: 0o600, flag: 'wx' });
|
||||
renameSync(adapterTemporary, adapterAbsolute);
|
||||
|
||||
const entry = {
|
||||
project_id: projectId,
|
||||
domain,
|
||||
domain_root: `${embeddedAppRoot}/domain`,
|
||||
manifest: adapterManifestPath,
|
||||
context_roots: [relativeRoot],
|
||||
shell_root: relativeRoot,
|
||||
harness_package: 'fpt-casan-sdd-harness',
|
||||
harness_version: '1.0.0',
|
||||
status: 'active',
|
||||
};
|
||||
const updated = { ...registry, projects: [...registry.projects, entry] };
|
||||
const temporary = `${PROJECT_REGISTRY}.${process.pid}.${randomUUID()}.tmp`;
|
||||
writeFileSync(temporary, `${JSON.stringify(updated, null, 2)}\n`, { encoding: 'utf8', mode: 0o600, flag: 'wx' });
|
||||
renameSync(temporary, PROJECT_REGISTRY);
|
||||
return { project_id: projectId, domain, domain_root: relativeRoot, context_roots: [relativeRoot] };
|
||||
return { project_id: projectId, domain, domain_root: entry.domain_root, manifest: adapterManifestPath, shell_root: relativeRoot, context_roots: [relativeRoot] };
|
||||
} catch (error) {
|
||||
if (createdRoot && absoluteRoot) rmSync(absoluteRoot, { recursive: true, force: true });
|
||||
if (error instanceof HttpException) throw error;
|
||||
@@ -379,7 +452,14 @@ export class GoalsService {
|
||||
}
|
||||
return relative;
|
||||
});
|
||||
return { project_id: projectId, domain: String(entry.domain ?? projectId), domain_root: domainRoot, context_roots: contextRoots };
|
||||
return {
|
||||
project_id: projectId,
|
||||
domain: String(entry.domain ?? projectId),
|
||||
domain_root: domainRoot,
|
||||
context_roots: contextRoots,
|
||||
manifest: entry.manifest ? String(entry.manifest) : undefined,
|
||||
shell_root: entry.shell_root ? String(entry.shell_root) : undefined,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user