From e891981b59f60a8544243385a2f820c649ac584f Mon Sep 17 00:00:00 2001 From: thanhnv Date: Wed, 8 Jul 2026 11:36:24 +0900 Subject: [PATCH] =?UTF-8?q?feat(plan-01):=20Phase=204b=20=E2=80=94=20exter?= =?UTF-8?q?nal=20callers=20to=20packages=20paths=20+=20registry=20(Task=20?= =?UTF-8?q?1.7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Point the canonical entry points at the new package layout; the .specify compat facade is retained as an intentional backward-compat layer (see below). - .gitea/workflows/{ci,harness-ci}.yml: invoke packages/casan-harness/scripts/bash|tests (state paths .specify/logs kept). CI now runs on the new structure. - infra/local-prod/docker-compose.yml: dashboard-server.py -> packages path (logs/alerts env stay under .specify state). - scripts/casan-step.mjs + run-casan-pipeline.mjs: resolve the harness under packages/casan-harness, falling back to .specify so the adversarial/sourcegen sandboxes (which stage a .specify/ tree) keep working; requirement input prefers apps/okr/domain/input. - project-registry.json: record the new layout (harness_root, state_root, governance_root, per-project domain_root) so Plan-06 can register a second app with its own domain. verify-harness-reuse.sh already resolves via CASAN_HARNESS_ROOT -> HARNESS_REUSE_VALID (3 projects). Facade decision: the .specify/{scripts,tests,security,...} symlinks are KEPT as a documented compat layer. A full hard cutover (removing them) still needs ~15 literal `.specify/...` refs repointed (loop_common/evidence-pack/secrets-scan config+test paths, run-casan-pipeline step scripts, and the signed policy-bundle.yaml path list which then needs manifest regen + re-sign). That is a scoped follow-up; the physical separation (code in packages/, domain in apps/, packages holds no domain data, single CASAN_* path indirection) is complete and the full gate is green via BOTH entry paths: PASS=64 FAIL=0 SKIP=3. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../.gitea/vault-setup-runbook.md | 12 ++++++------ AINative_OKR_CASAN5/.gitea/workflows/ci.yml | 18 +++++++++--------- .../.gitea/workflows/harness-ci.yml | 2 +- .../infra/local-prod/docker-compose.yml | 2 +- .../casan-harness/level5/project-registry.json | 9 +++++++++ AINative_OKR_CASAN5/scripts/casan-step.mjs | 15 ++++++++++++--- .../scripts/run-casan-pipeline.mjs | 10 +++++++--- 7 files changed, 45 insertions(+), 23 deletions(-) diff --git a/AINative_OKR_CASAN5/.gitea/vault-setup-runbook.md b/AINative_OKR_CASAN5/.gitea/vault-setup-runbook.md index 5f60e73..1c3adf2 100644 --- a/AINative_OKR_CASAN5/.gitea/vault-setup-runbook.md +++ b/AINative_OKR_CASAN5/.gitea/vault-setup-runbook.md @@ -151,15 +151,15 @@ export VAULT_TOKEN= cd Output_CASAN5_REFINED/AINative_OKR_CASAN5 # Test sign-policy-bundle with Vault -bash .specify/scripts/bash/sign-policy-bundle.sh sign +bash packages/casan-harness/scripts/bash/sign-policy-bundle.sh sign # Expected: POLICY_BUNDLE_SIGNED ... key_backend=vault-kms # Test sign-audit-head -bash .specify/scripts/bash/sign-audit-head.sh +bash packages/casan-harness/scripts/bash/sign-audit-head.sh # Expected: SIGN_AUDIT_HEAD_OK ... anchor=vault-kms # Verify audit chain -bash .specify/scripts/bash/verify-audit-chain.sh +bash packages/casan-harness/scripts/bash/verify-audit-chain.sh # Expected: AUDIT_CHAIN_VALID anchor=signed ``` @@ -168,9 +168,9 @@ bash .specify/scripts/bash/verify-audit-chain.sh ## Step 7 — Push code to trigger CI with Vault ```bash -git add .gitea/ .specify/scripts/bash/vault-kms.sh \ - .specify/scripts/bash/sign-audit-head.sh \ - .specify/scripts/bash/sign-policy-bundle.sh +git add .gitea/ packages/casan-harness/scripts/bash/vault-kms.sh \ + packages/casan-harness/scripts/bash/sign-audit-head.sh \ + packages/casan-harness/scripts/bash/sign-policy-bundle.sh git commit -m "feat(H5): Vault KMS for policy + audit chain signing" git push gitea main ``` diff --git a/AINative_OKR_CASAN5/.gitea/workflows/ci.yml b/AINative_OKR_CASAN5/.gitea/workflows/ci.yml index 6d0f8ee..5780a61 100644 --- a/AINative_OKR_CASAN5/.gitea/workflows/ci.yml +++ b/AINative_OKR_CASAN5/.gitea/workflows/ci.yml @@ -83,9 +83,9 @@ jobs: # Non-blocking: if Vault is unreachable, scripts fall back gracefully. run: | if curl -sf "$VAULT_ADDR/v1/sys/health" >/dev/null 2>&1; then - bash .specify/scripts/bash/vault-kms.sh enable-transit - bash .specify/scripts/bash/vault-kms.sh ensure-key casan-policy-key - bash .specify/scripts/bash/vault-kms.sh ensure-key casan-audit-key + bash packages/casan-harness/scripts/bash/vault-kms.sh enable-transit + bash packages/casan-harness/scripts/bash/vault-kms.sh ensure-key casan-policy-key + bash packages/casan-harness/scripts/bash/vault-kms.sh ensure-key casan-audit-key echo "VAULT_KMS_READY" else echo "VAULT_KMS_SKIP (unreachable — will use local-file fallback)" @@ -93,27 +93,27 @@ jobs: - name: Run CASAN4 harness tests (35 tests) # This clears .specify/logs/ and rebuilds audit.jsonl from scratch. - run: bash .specify/tests/run-casan4-harness-tests.sh + run: bash packages/casan-harness/tests/run-casan4-harness-tests.sh - name: Run adversarial harness tests (44 tests) - run: bash .specify/tests/adversarial-harness-tests.sh + run: bash packages/casan-harness/tests/adversarial-harness-tests.sh - name: Sign audit chain head via Vault KMS (H5) # After harness tests rebuild audit.jsonl, sign the head so that # verify-audit-chain.sh reports "anchor=signed" (not "anchor=unsigned"). - run: bash .specify/scripts/bash/sign-audit-head.sh + run: bash packages/casan-harness/scripts/bash/sign-audit-head.sh - name: Sign policy bundle via Vault KMS (H5) - run: bash .specify/scripts/bash/sign-policy-bundle.sh sign + run: bash packages/casan-harness/scripts/bash/sign-policy-bundle.sh sign - name: Verify audit chain (anchor=signed expected in CI) - run: bash .specify/scripts/bash/verify-audit-chain.sh + run: bash packages/casan-harness/scripts/bash/verify-audit-chain.sh - name: Security gate — aggregate verdict (PASS=7 SKIP=1 FAIL=0) # Ollama-dependent checks auto-SKIP (not FAIL) when Ollama is not reachable. # security-gate.sh re-runs harness + adversarial + verify + scan + frontend. # Exit 0 only when FAIL=0. - run: bash .specify/scripts/bash/security-gate.sh + run: bash packages/casan-harness/scripts/bash/security-gate.sh - name: Upload test evidence if: always() diff --git a/AINative_OKR_CASAN5/.gitea/workflows/harness-ci.yml b/AINative_OKR_CASAN5/.gitea/workflows/harness-ci.yml index 59c5a24..c69373f 100644 --- a/AINative_OKR_CASAN5/.gitea/workflows/harness-ci.yml +++ b/AINative_OKR_CASAN5/.gitea/workflows/harness-ci.yml @@ -34,4 +34,4 @@ jobs: - name: Run CASAN harness gate working-directory: AINative_OKR_CASAN5 - run: bash .specify/scripts/bash/ci-harness-gate.sh + run: bash packages/casan-harness/scripts/bash/ci-harness-gate.sh diff --git a/AINative_OKR_CASAN5/infra/local-prod/docker-compose.yml b/AINative_OKR_CASAN5/infra/local-prod/docker-compose.yml index b4fed97..cc63efa 100644 --- a/AINative_OKR_CASAN5/infra/local-prod/docker-compose.yml +++ b/AINative_OKR_CASAN5/infra/local-prod/docker-compose.yml @@ -103,7 +103,7 @@ services: dashboard: image: python:3.12-slim working_dir: /workspace - command: ["python", ".specify/scripts/bash/dashboard-server.py", "8787"] + command: ["python", "packages/casan-harness/scripts/bash/dashboard-server.py", "8787"] environment: CASAN_DASHBOARD_BIND: 0.0.0.0 CASAN_DASHBOARD_STALE_S: "315360000" diff --git a/AINative_OKR_CASAN5/packages/casan-harness/level5/project-registry.json b/AINative_OKR_CASAN5/packages/casan-harness/level5/project-registry.json index e115755..5e4adcb 100644 --- a/AINative_OKR_CASAN5/packages/casan-harness/level5/project-registry.json +++ b/AINative_OKR_CASAN5/packages/casan-harness/level5/project-registry.json @@ -1,9 +1,16 @@ { "registry": "fpt-casan-harness-adoption", + "layout": { + "note": "Plan-01 restructure: reusable harness is a standalone package; each project keeps its domain data separate. A new project (Plan-06) registers here with its own domain_root.", + "harness_root": "packages/casan-harness", + "state_root": ".specify", + "governance_root": ".specify/level5/central-governance" + }, "projects": [ { "project_id": "AINative_OKR_CASAN4", "domain": "SDD OKR", + "domain_root": "apps/okr/domain", "harness_package": "fpt-casan-sdd-harness", "harness_version": "1.0.0", "status": "active" @@ -11,6 +18,7 @@ { "project_id": "CASAN_DEMO_PROJECT_A", "domain": "Demo SDLC", + "domain_root": "apps/demo-sdlc/domain", "harness_package": "fpt-casan-sdd-harness", "harness_version": "1.0.0", "status": "registered" @@ -18,6 +26,7 @@ { "project_id": "CASAN_DEMO_PROJECT_B", "domain": "Demo QA", + "domain_root": "apps/demo-qa/domain", "harness_package": "fpt-casan-sdd-harness", "harness_version": "1.0.0", "status": "registered" diff --git a/AINative_OKR_CASAN5/scripts/casan-step.mjs b/AINative_OKR_CASAN5/scripts/casan-step.mjs index 9128b61..65af4e6 100644 --- a/AINative_OKR_CASAN5/scripts/casan-step.mjs +++ b/AINative_OKR_CASAN5/scripts/casan-step.mjs @@ -1,12 +1,18 @@ import { spawnSync } from 'node:child_process'; -import { mkdirSync, readFileSync, writeFileSync, unlinkSync } from 'node:fs'; +import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; // WP-B (H3): model judge gate helpers — AND(rule, model); SKIP is non-blocking. const __filename = fileURLToPath(import.meta.url); -const SCRIPTS_DIR = join(dirname(__filename), '..', '.specify', 'scripts', 'bash'); +// Plan-01: harness code lives in packages/casan-harness/; fall back to the .specify +// compat path so the adversarial/sourcegen sandboxes (which stage a .specify/ tree) +// keep working when this file is copied into a temp root. +const __appRoot = join(dirname(__filename), '..'); +const SCRIPTS_DIR = existsSync(join(__appRoot, 'packages', 'casan-harness', 'scripts', 'bash')) + ? join(__appRoot, 'packages', 'casan-harness', 'scripts', 'bash') + : join(__appRoot, '.specify', 'scripts', 'bash'); // Shared log taxonomy (CASAN_LOG_LEVEL, see scripts/casan-log.mjs). The // adversarial T1 test copies this file alone into a temp tree, so the logger @@ -180,7 +186,10 @@ const dirs = [ ]; dirs.forEach((dir) => mkdirSync(dir, { recursive: true })); -const requirement = readFileSync('docs/input/okr-requirement.md', 'utf8'); +const requirement = readFileSync( + existsSync('apps/okr/domain/input/okr-requirement.md') + ? 'apps/okr/domain/input/okr-requirement.md' + : 'docs/input/okr-requirement.md', 'utf8'); const architecture = readFileSync('docs/technical_architecture.md', 'utf8'); function write(path, content) { diff --git a/AINative_OKR_CASAN5/scripts/run-casan-pipeline.mjs b/AINative_OKR_CASAN5/scripts/run-casan-pipeline.mjs index b3d3aab..c80bb9d 100644 --- a/AINative_OKR_CASAN5/scripts/run-casan-pipeline.mjs +++ b/AINative_OKR_CASAN5/scripts/run-casan-pipeline.mjs @@ -1,5 +1,5 @@ import { execFileSync } from 'node:child_process'; -import { appendFileSync, copyFileSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from 'node:fs'; +import { existsSync, appendFileSync, copyFileSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from 'node:fs'; import { dirname, join } from 'node:path'; import { enabled, log, redact, LOG_LEVEL } from './casan-log.mjs'; @@ -10,6 +10,10 @@ const dryRun = process.argv.includes('--dry-run'); const featureId = '001-okr-web-app'; const root = process.cwd(); +// Plan-01: harness relocated to packages/casan-harness/; fall back to the .specify facade. +const HARNESS = existsSync(join(root, 'packages/casan-harness/scripts/bash/casan-harness.sh')) + ? 'packages/casan-harness/scripts/bash/casan-harness.sh' + : '.specify/scripts/bash/casan-harness.sh'; const logDir = `docs/output/output_logs/${featureId}`; const casanDir = `${logDir}/casan`; const reportsDir = `${logDir}/reports`; @@ -172,7 +176,7 @@ function runHarness({ id, agent, step, attempt = '1' }) { const startedAt = Date.now(); try { execFileSync( - '.specify/scripts/bash/casan-harness.sh', + HARNESS, [input, output, `agent_step_${id}`, '--', 'node', 'scripts/casan-step.mjs', step, attempt], { cwd: root, @@ -245,7 +249,7 @@ function runDryStep({ diagram, agent, attempt = '1', verdict = 'APPROVED', extra const stubCmd = `printf '# %s dry-run stub artifact\\n\\nverdict: %s\\n' '${diagram}' '${verdict}' > "$CASAN_OUTPUT"`; const startedAt = Date.now(); execFileSync( - '.specify/scripts/bash/casan-harness.sh', + HARNESS, [input, output, `agent_step_${id}`, '--', 'bash', '-c', stubCmd], { cwd: root,