feat(packaging): level-based source hub — Core + DevKit packaging, Platform/Enterprise scaffold
Organize CASAN as a reusable source hub with SPLIT releases so downstream adopts only the level it needs (packaging/levels.json is the single source of truth). Implemented now: - Level 1 Core: bin/casan CLI (run/gate/test/verify/reuse/dashboard) + VERSION. - Level 2 DevKit: packages/casan-devkit (install.sh, Dockerfile.harness, templates: project scaffold, domain-pack, gitea-workflow). - scripts/package-release.sh core|devkit|platform|all-in-one-demo — builds split bundles into dist/ (BUNDLE-MANIFEST + SHA256SUMS); platform is stamped PREVIEW/INCOMPLETE; enterprise (future) is REFUSED (exit 3, no fake-complete package). Bundles verified: extract → bin/casan works, deterministic + domain suites pass, casan reuse VALID. - docs/packaging: CASAN_PACKAGING_PLAN + ADOPTION + CI + DOMAIN_PACK + GITEA_PACKAGE + DOCKER. Structure + docs only: - Level 3 packages/casan-platform (dashboard exists; control-panel/viewers pending). - Level 4 packages/casan-enterprise (RBAC/tenant/KMS/WORM/approval exist in core; governed console pending). No Chat Console/RBAC-console/tenant-console/model-mgmt built in this task. Harness change (enables extracted bundles to self-resolve): casan-paths.sh + the Python project_root() walk-ups now accept a second root marker `packages/casan-harness` in addition to `.specify`, so a freshly-unpacked core/devkit/demo bundle (no `.specify` yet) roots correctly and creates state on first run. In an adopted repo `.specify` still matches first. policy-bundle.yaml paths corrected to packages/casan-harness (re-signed). Full gate 64/0/3. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
4918012199
commit
8c20cfde9f
@@ -0,0 +1,74 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# CASAN — lightweight CLI wrapper (Level 1 Core Harness).
|
||||||
|
#
|
||||||
|
# Locates the harness package relative to this script (works both in the full
|
||||||
|
# source hub and in an extracted casan-core / casan-devkit bundle) and dispatches
|
||||||
|
# to the harness bash entrypoints. No dependencies beyond bash + python3.
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
# --- locate the harness root (dir containing scripts/bash/casan-harness.sh) ----
|
||||||
|
_self="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
_find_harness() {
|
||||||
|
# 1) sibling packages/casan-harness (source hub or bundle root layout)
|
||||||
|
local c
|
||||||
|
for c in "$_self/../packages/casan-harness" "$_self/../casan-harness" "$_self/packages/casan-harness"; do
|
||||||
|
[[ -f "$c/scripts/bash/casan-harness.sh" ]] && { (cd "$c" && pwd); return 0; }
|
||||||
|
done
|
||||||
|
# 2) walk up looking for it
|
||||||
|
local d="$_self"
|
||||||
|
while [[ "$d" != "/" ]]; do
|
||||||
|
[[ -f "$d/packages/casan-harness/scripts/bash/casan-harness.sh" ]] && { echo "$d/packages/casan-harness"; return 0; }
|
||||||
|
d="$(dirname "$d")"
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
HARNESS="${CASAN_HARNESS_ROOT:-$(_find_harness)}"
|
||||||
|
if [[ -z "${HARNESS:-}" || ! -d "$HARNESS" ]]; then
|
||||||
|
echo "casan: cannot locate packages/casan-harness (set CASAN_HARNESS_ROOT)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
BASH_DIR="$HARNESS/scripts/bash"
|
||||||
|
TESTS_DIR="$HARNESS/tests"
|
||||||
|
VERSION_FILE="$_self/../VERSION"
|
||||||
|
[[ -f "$VERSION_FILE" ]] || VERSION_FILE="$HARNESS/../../VERSION"
|
||||||
|
|
||||||
|
version() { printf 'casan %s\n' "$( [[ -f "$VERSION_FILE" ]] && cat "$VERSION_FILE" || echo 'unknown')"; }
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<EOF
|
||||||
|
casan — CASAN governance harness CLI ($(version))
|
||||||
|
|
||||||
|
Usage: casan <command> [args]
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
run <in> <out> [action] [-- cmd...] Run a step through the harness (H4→H5→H6→exec→H4-out)
|
||||||
|
gate Run the full CI harness gate (all suites)
|
||||||
|
test Run the core harness test suite (run-casan4)
|
||||||
|
verify Verify audit chain + tool audit + policy bundle
|
||||||
|
reuse Verify multi-project harness reuse (registry)
|
||||||
|
dashboard [port] Serve the AgentOps dashboard (default 8787)
|
||||||
|
version Print version
|
||||||
|
help This help
|
||||||
|
|
||||||
|
Env: CASAN_HARNESS_ROOT overrides harness location.
|
||||||
|
Harness: $HARNESS
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd="${1:-help}"; shift || true
|
||||||
|
case "$cmd" in
|
||||||
|
run) exec bash "$BASH_DIR/casan-harness.sh" "$@" ;;
|
||||||
|
gate) exec bash "$BASH_DIR/ci-harness-gate.sh" "$@" ;;
|
||||||
|
test) exec bash "$TESTS_DIR/run-casan4-harness-tests.sh" "$@" ;;
|
||||||
|
verify)
|
||||||
|
rc=0
|
||||||
|
bash "$BASH_DIR/verify-audit-chain.sh" "$@" || rc=$?
|
||||||
|
bash "$BASH_DIR/verify-tool-audit.sh" || rc=$?
|
||||||
|
bash "$BASH_DIR/sign-policy-bundle.sh" verify || rc=$?
|
||||||
|
exit $rc ;;
|
||||||
|
reuse) exec bash "$BASH_DIR/verify-harness-reuse.sh" "$@" ;;
|
||||||
|
dashboard) exec bash "$BASH_DIR/dashboard-serve.sh" "$@" ;;
|
||||||
|
version|-v|--version) version ;;
|
||||||
|
help|-h|--help) usage ;;
|
||||||
|
*) echo "casan: unknown command '$cmd'" >&2; usage >&2; exit 64 ;;
|
||||||
|
esac
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
# CASAN Adoption Guide
|
||||||
|
|
||||||
|
How a downstream project adopts the CASAN governance harness. Adoption is **config +
|
||||||
|
domain only** — you never edit gate logic (H1→H7).
|
||||||
|
|
||||||
|
## Option A — DevKit install (recommended)
|
||||||
|
From a CASAN source hub or an extracted `casan-devkit` bundle:
|
||||||
|
```bash
|
||||||
|
packages/casan-devkit/install.sh --target ../my-project --project ticketing --domain "Ticketing"
|
||||||
|
```
|
||||||
|
This copies the core harness + `bin/casan` into `../my-project`, scaffolds
|
||||||
|
`apps/ticketing/domain/` from the domain-pack template, adds `.gitea/workflows/casan-ci.yml`,
|
||||||
|
and registers the project in `project-registry.json`.
|
||||||
|
|
||||||
|
## Option B — Core tarball (harness-only / CI gate)
|
||||||
|
```bash
|
||||||
|
tar -xzf casan-core-v1.0.0.tar.gz
|
||||||
|
cp -R casan-core-v1.0.0/{packages,bin,VERSION} /path/to/project/
|
||||||
|
```
|
||||||
|
Then create `apps/<project>/domain/` yourself (see `DOMAIN_PACK_GUIDE.md`).
|
||||||
|
|
||||||
|
## Option C — Docker (no install into repo)
|
||||||
|
```bash
|
||||||
|
docker run --rm -v "$PWD":/workspace -w /workspace casan-harness:1.0.0 casan gate
|
||||||
|
```
|
||||||
|
See `DOCKER_GUIDE.md`.
|
||||||
|
|
||||||
|
## After install
|
||||||
|
1. Requirement → `apps/<project>/domain/input/requirement.md` (keep the `| FR-xx |` table).
|
||||||
|
2. Golden baseline → `apps/<project>/domain/golden-runs/<artifact>.golden.txt`.
|
||||||
|
3. Corpus → `apps/<project>/domain/corpus/` (redteam + benign) for H4 scoring.
|
||||||
|
4. Requirement→code→test map → `apps/<project>/domain/traceability-map.json`.
|
||||||
|
5. Run:
|
||||||
|
```bash
|
||||||
|
CASAN_DOMAIN_ROOT=apps/<project>/domain bin/casan gate # full governance gate
|
||||||
|
bin/casan run in.txt out.txt my_step -- <command> # one governed step
|
||||||
|
bin/casan reuse # HARNESS_REUSE_VALID
|
||||||
|
```
|
||||||
|
|
||||||
|
## Path model (what lives where)
|
||||||
|
- **Harness code** → `packages/casan-harness/` (never edited by adopters).
|
||||||
|
- **Your domain data** → `apps/<project>/domain/` (via `CASAN_DOMAIN_ROOT`).
|
||||||
|
- **Runtime state** → `.specify/` (logs, audit, governance — created on first run).
|
||||||
|
Paths resolve via `packages/casan-harness/scripts/bash/casan-paths.sh` (marker walk-up:
|
||||||
|
`.specify` or `packages/casan-harness`), so a freshly-extracted bundle works immediately.
|
||||||
|
|
||||||
|
## Proving reuse
|
||||||
|
Two+ projects sharing the same harness package/version → `bin/casan reuse` prints
|
||||||
|
`HARNESS_REUSE_VALID ... project_count=N`. This is the evidence that the harness is genuinely
|
||||||
|
reusable, not copy-pasted. See `docs/plans/CASAN_PLAN_06_ONBOARD.md`.
|
||||||
|
|
||||||
|
## Upgrading
|
||||||
|
Re-run `install.sh` from a newer DevKit (or re-extract a newer core tarball). Your
|
||||||
|
`apps/<project>/domain/` and `.specify/` state are untouched — only `packages/casan-harness/`
|
||||||
|
+ `bin/casan` are replaced. Keep `harness_version` aligned across projects for reuse to count.
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
# CASAN Packaging Plan — Level-Based Source Hub
|
||||||
|
|
||||||
|
CASAN is **not** packaged as "core only". This repository is a **reusable source hub** that
|
||||||
|
holds (or scaffolds) all major CASAN components, and **releases are split by level** so a
|
||||||
|
downstream project adopts only the level it needs. Single source of truth for bundle
|
||||||
|
contents + maturity: [`packaging/levels.json`](../../packaging/levels.json).
|
||||||
|
|
||||||
|
## The four levels
|
||||||
|
|
||||||
|
| Lvl | Package names | Status | What it is |
|
||||||
|
|---|---|:--:|---|
|
||||||
|
| **1 — Core Harness** | `casan-core`, `casan-harness` | ✅ implemented | H1–H7 harness, security + action gates, evidence pack, audit, cost/telemetry, hardening tests, policy/config defaults, `bin/casan` CLI |
|
||||||
|
| **2 — DevKit / Adoption Kit** | `casan-devkit`, `casan-project-kit` | ✅ implemented | Level 1 + project templates, domain-pack scaffold, Gitea workflow template, harness Dockerfile, install script, adoption/CI/domain-pack guides |
|
||||||
|
| **3 — Platform Components** | `casan-platform`, `casan-control-panel` | 🟡 preview | Control Panel, Dashboard, Evidence/Attack/Run-History viewers, read-only Ask CASAN, Gitea webhook. **Only the AgentOps dashboard exists today.** |
|
||||||
|
| **4 — Enterprise / Governed Console** | `casan-enterprise`, `casan-governed-console` | 📋 future | Governed Chat Console, Prompt Router, Model-Provider Mgmt, Operator/Codegen modes, Agent/Skill Registry, RBAC, approval, tenant isolation, KMS/WORM, policy versioning |
|
||||||
|
|
||||||
|
Levels are cumulative: DevKit extends Core, Platform extends DevKit, Enterprise extends
|
||||||
|
Platform.
|
||||||
|
|
||||||
|
## Packaging principle
|
||||||
|
The source hub may contain all levels, **but releases must be split**. Do NOT force a
|
||||||
|
downstream project to install everything. A level that is not implemented must **fail
|
||||||
|
clearly** or be stamped **PREVIEW/INCOMPLETE** — never a fake-complete package.
|
||||||
|
|
||||||
|
## Release artifacts
|
||||||
|
Built by [`scripts/package-release.sh`](../../scripts/package-release.sh) into `dist/`:
|
||||||
|
|
||||||
|
| Command | Artifact | Status |
|
||||||
|
|---|---|:--:|
|
||||||
|
| `package-release.sh core` | `casan-core-vX.Y.Z.tar.gz` | ✅ builds |
|
||||||
|
| `package-release.sh devkit` | `casan-devkit-vX.Y.Z.tar.gz` | ✅ builds |
|
||||||
|
| `package-release.sh platform` | `casan-platform-preview-vX.Y.Z.tar.gz` | 🟡 preview (stamped) |
|
||||||
|
| `package-release.sh all-in-one-demo` | `casan-all-in-one-demo-vX.Y.Z.tar.gz` | ✅ builds (full runnable snapshot) |
|
||||||
|
| `package-release.sh enterprise` | — | 📋 refused (exit 3, future) |
|
||||||
|
|
||||||
|
Docker images (see `DOCKER_GUIDE.md`): `casan-harness:X.Y.Z` ✅ · `casan-platform:X.Y.Z` 🟡 preview · `casan-enterprise:X.Y.Z` 📋 future.
|
||||||
|
|
||||||
|
Every bundle carries `BUNDLE-MANIFEST.txt` + `SHA256SUMS`; preview bundles also carry
|
||||||
|
`PREVIEW-INCOMPLETE.txt`.
|
||||||
|
|
||||||
|
## Who adopts what
|
||||||
|
- **Governance-harness-only / BJT initial / CI gate** → `casan-core`.
|
||||||
|
- **New project adopting CASAN** → `casan-devkit` (install.sh scaffolds domain + CI).
|
||||||
|
- **Want dashboards/visibility** → `casan-platform` (preview; dashboard today).
|
||||||
|
- **Enterprise governed console** → future; building blocks (RBAC/tenant/KMS/WORM/approval)
|
||||||
|
already live in core.
|
||||||
|
|
||||||
|
## Repository map
|
||||||
|
```
|
||||||
|
packages/casan-harness/ # L1 core (implemented)
|
||||||
|
packages/casan-devkit/ # L2 adoption kit (implemented)
|
||||||
|
packages/casan-platform/ # L3 structure-only (preview; README)
|
||||||
|
packages/casan-enterprise/ # L4 structure-only (future; README)
|
||||||
|
bin/casan # CLI
|
||||||
|
scripts/package-release.sh # release packager
|
||||||
|
packaging/levels.json # bundle contents + maturity (source of truth)
|
||||||
|
docs/packaging/ # this plan + adoption/CI/domain-pack/gitea/docker guides
|
||||||
|
```
|
||||||
|
|
||||||
|
## Implemented now vs future
|
||||||
|
- **Implemented now:** Level 1 Core packaging, Level 2 DevKit packaging, release script,
|
||||||
|
`bin/casan`, templates, docs, all-in-one-demo bundle.
|
||||||
|
- **Structure + docs only:** Level 3 Platform (dashboard exists; rest scaffolded),
|
||||||
|
Level 4 Enterprise (RBAC/tenant/KMS/WORM/approval exist in core; governed console not built).
|
||||||
|
- **Not built in this task (do not assume present):** Governed Chat Console, Prompt Mode
|
||||||
|
Router, Model Provider Management, Operator/Codegen modes, Agent/Skill Registry, RBAC/
|
||||||
|
tenant *console* UX.
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# CASAN CI Guide (Gitea Actions)
|
||||||
|
|
||||||
|
Wire the CASAN governance gate into your project's CI so every push/PR is governed.
|
||||||
|
|
||||||
|
## 1. Add the workflow
|
||||||
|
DevKit `install.sh` already drops `.gitea/workflows/casan-ci.yml`. To add manually, copy the
|
||||||
|
template:
|
||||||
|
```bash
|
||||||
|
cp packages/casan-devkit/templates/gitea-workflow/ci.yml .gitea/workflows/casan-ci.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. What it runs
|
||||||
|
```yaml
|
||||||
|
- bash packages/casan-harness/scripts/bash/ci-harness-gate.sh # all governance suites
|
||||||
|
- bash packages/casan-harness/scripts/bash/verify-audit-chain.sh
|
||||||
|
- bash packages/casan-harness/scripts/bash/sign-policy-bundle.sh verify
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. Environment knobs
|
||||||
|
| Var | Default | Use |
|
||||||
|
|---|---|---|
|
||||||
|
| `CASAN_CI_RUN_FRONTEND` | 0 | 1 if you have a frontend workspace |
|
||||||
|
| `CASAN_CI_RUN_BACKEND` | 0 | 1 to run backend tests |
|
||||||
|
| `CASAN_CI_RUN_INFRA_LAB` | 0 | 1 to run the Docker infra lab |
|
||||||
|
| `CASAN_CI_STEP_TIMEOUT_SEC` | 600 | raise to 1200 — some suites are model-backed and run ~450s; 600 flakes under load |
|
||||||
|
| `CASAN_CI_SUITE_FILTER` | — | regex to run a subset of suites |
|
||||||
|
| `CASAN_DOMAIN_ROOT` | apps/okr/domain | point at your project's domain |
|
||||||
|
|
||||||
|
## 4. Runner
|
||||||
|
Uses `runs-on: ci-runner` (a self-hosted Gitea Actions runner). To set one up on your host,
|
||||||
|
see `.gitea/vps-setup-runbook.md` and `scripts/setup-ci-runner.sh`. The runner needs
|
||||||
|
`bash`, `python3`, `openssl` (and `node`/`npm` only if you enable frontend/backend tests).
|
||||||
|
|
||||||
|
## 5. Expected result
|
||||||
|
`CI_GATE_SUMMARY PASS=<n> FAIL=0 SKIP=<k>`. Any FAIL fails the job (exit 1). The gate is
|
||||||
|
**fail-closed**: missing signatures/policy in enforced mode (`CASAN_PROFILE=prod` or
|
||||||
|
`CASAN_VERIFY_STRICT=1`) also fail.
|
||||||
|
|
||||||
|
## 6. This repo's own CI
|
||||||
|
The source hub's active pipeline is `.gitea/workflows/ci.yml` (frontend tests → security
|
||||||
|
gate → deploy). It already runs the gate from the promoted root layout
|
||||||
|
(`packages/casan-harness/...`). Use it as a worked example.
|
||||||
|
|
||||||
|
## Tips
|
||||||
|
- Keep the gate green as an invariant; every new control must ship a fail-able adversarial test.
|
||||||
|
- Do not stage runtime log artifacts (`.specify/logs`, evidence) produced by gate runs.
|
||||||
|
- For a quick local pre-push check: `bin/casan gate` (or a filtered subset).
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
# CASAN Docker Guide
|
||||||
|
|
||||||
|
Run the CASAN governance harness as a container against any mounted repo — no install into
|
||||||
|
the target project.
|
||||||
|
|
||||||
|
## Image: `casan-harness` (Level 1/2)
|
||||||
|
Dockerfile: [`packages/casan-devkit/Dockerfile.harness`](../../packages/casan-devkit/Dockerfile.harness).
|
||||||
|
Minimal Debian + bash + python3 + openssl + git. Ships the core harness + `bin/casan`.
|
||||||
|
|
||||||
|
### Build
|
||||||
|
```bash
|
||||||
|
V=$(cat VERSION)
|
||||||
|
docker build -f packages/casan-devkit/Dockerfile.harness -t casan-harness:$V .
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run the gate on a project
|
||||||
|
```bash
|
||||||
|
docker run --rm -v "$PWD":/workspace -w /workspace casan-harness:1.0.0 gate
|
||||||
|
# (ENTRYPOINT is `casan`, so the CMD is a casan subcommand)
|
||||||
|
docker run --rm -v "$PWD":/workspace casan-harness:1.0.0 reuse
|
||||||
|
docker run --rm -v "$PWD":/workspace casan-harness:1.0.0 \
|
||||||
|
run in.txt out.txt my_step -- echo hello
|
||||||
|
```
|
||||||
|
The harness inside the image lives at `/opt/casan` (`CASAN_HARNESS_ROOT` preset); your
|
||||||
|
project is mounted at `/workspace`. Domain data is read from the mounted repo
|
||||||
|
(`apps/<project>/domain` via `CASAN_DOMAIN_ROOT`).
|
||||||
|
|
||||||
|
### With a domain root
|
||||||
|
```bash
|
||||||
|
docker run --rm -v "$PWD":/workspace -w /workspace \
|
||||||
|
-e CASAN_DOMAIN_ROOT=apps/ticketing/domain casan-harness:1.0.0 gate
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
- **State** (`.specify/logs`, audit) is written under `/workspace` (your mounted repo), so it
|
||||||
|
persists on the host and is inspectable after the run.
|
||||||
|
- **Enforced mode:** add `-e CASAN_PROFILE=prod` (or `-e CASAN_VERIFY_STRICT=1`) to make
|
||||||
|
missing signatures fail-closed.
|
||||||
|
- **No app runtime:** this image runs the *harness*, not the OKR app. For the app, use the
|
||||||
|
project's own `Dockerfile.backend` / `Dockerfile.frontend` + `docker-compose.prod.yml`.
|
||||||
|
|
||||||
|
## Other images
|
||||||
|
| Image | Status | Notes |
|
||||||
|
|---|:--:|---|
|
||||||
|
| `casan-harness:X.Y.Z` | ✅ | this guide |
|
||||||
|
| `casan-platform:X.Y.Z` | 🟡 preview | dashboard only today; build from platform preview when needed |
|
||||||
|
| `casan-enterprise:X.Y.Z` | 📋 future | not built — governed console does not exist yet |
|
||||||
|
|
||||||
|
## Publish to Gitea container registry
|
||||||
|
See `GITEA_PACKAGE_GUIDE.md` §3 (`docker login` + `docker push` to the Gitea registry).
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
# CASAN Domain Pack Guide
|
||||||
|
|
||||||
|
A **Domain Pack** is the per-project data the harness needs to govern YOUR domain. It lives
|
||||||
|
at `apps/<project>/domain/` and is selected via `CASAN_DOMAIN_ROOT`. The harness code
|
||||||
|
(`packages/casan-harness/`) never contains domain data — this is what makes it reusable.
|
||||||
|
|
||||||
|
Scaffold template: `packages/casan-devkit/templates/domain-pack/`.
|
||||||
|
|
||||||
|
## Layout
|
||||||
|
```
|
||||||
|
apps/<project>/domain/
|
||||||
|
├── domain-pack.yaml # manifest describing this pack
|
||||||
|
├── input/
|
||||||
|
│ ├── requirement.md # FR-xx table drives the traceability gate
|
||||||
|
│ └── architecture.md # optional tech-stack/context
|
||||||
|
├── golden-runs/
|
||||||
|
│ └── <artifact>.golden.txt # baseline for drift detection (H7)
|
||||||
|
├── corpus/
|
||||||
|
│ ├── redteam-corpus.jsonl # attack vectors (H4 recall) — one JSON per line
|
||||||
|
│ ├── redteam-vectors.jsonl # vectors for benign-fp-report / redteam metrics
|
||||||
|
│ └── benign-corpus/*.txt # benign samples (H4 false-positive budget)
|
||||||
|
└── traceability-map.json # FR-xx → code files + test files (+ optional symbols/lines)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Each piece
|
||||||
|
- **input/requirement.md** — must contain a `| FR-xx | ... |` table. The traceability gate
|
||||||
|
(Plan-10) requires every `FR-xx` to map to ≥1 existing code file and ≥1 test file. No
|
||||||
|
secrets/PII (the H4 input scan blocks them).
|
||||||
|
- **golden-runs/** — a canonical "good" artifact per pipeline output. `drift-detect` compares
|
||||||
|
a run against it; similarity 1.0 = no drift. Real drift detection is proven separately.
|
||||||
|
- **corpus/** — red-team attack vectors (scored for H4 recall) + a benign corpus (bounds the
|
||||||
|
false-positive rate). Domain-specific injections make the H4 score meaningful.
|
||||||
|
- **traceability-map.json** — `{"FR-01": {"name": "...", "code": ["backend/src/...", {"file":"...","symbols":["Foo"],"lines":[12]}], "tests": ["..."]}}`. Symbol/line refs are
|
||||||
|
optional but tighten the gate.
|
||||||
|
- **domain-pack.yaml** — documents the above + optional threshold overrides.
|
||||||
|
|
||||||
|
## Wire it up
|
||||||
|
```bash
|
||||||
|
export CASAN_DOMAIN_ROOT=apps/<project>/domain
|
||||||
|
bin/casan gate # runs domain-dependent suites against your pack
|
||||||
|
```
|
||||||
|
`domain_root` is also recorded per-project in `packages/casan-harness/level5/project-registry.json`
|
||||||
|
so `bin/casan reuse` can prove multi-project reuse.
|
||||||
|
|
||||||
|
## Reference example
|
||||||
|
The OKR app's own pack is the worked example: `apps/okr/domain/` (golden-runs, corpus,
|
||||||
|
input, traceability-map). Copy its shape for your domain.
|
||||||
|
|
||||||
|
## Toward Plan-12 (Domain Pack SDK)
|
||||||
|
Today a pack is a directory + `CASAN_DOMAIN_ROOT`. Plan-12 will make it fully declarative
|
||||||
|
(register a pack by manifest, no manual wiring). The `domain-pack.yaml` here is the seed of
|
||||||
|
that manifest.
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
# CASAN on Gitea — Source Hub + Package Registry
|
||||||
|
|
||||||
|
This repo uses the existing Gitea server (`ssh://git@161.33.139.73:2222/admin/casan5.git`)
|
||||||
|
as the **source hub**. Gitea also ships a **package registry** (generic files) and a
|
||||||
|
**container registry**, so CASAN release bundles and Docker images can live next to the
|
||||||
|
source. This guide shows the release flow. No secrets are committed; use a Gitea token/PAT.
|
||||||
|
|
||||||
|
> Gitea host below is written as `$GITEA` (e.g. `http://161.33.139.73:3000`). Set
|
||||||
|
> `GITEA_TOKEN` to a personal access token with `write:package` scope. Adjust `admin`/`casan5`
|
||||||
|
> to your org/repo.
|
||||||
|
|
||||||
|
## 1. Build the bundles
|
||||||
|
```bash
|
||||||
|
scripts/package-release.sh core
|
||||||
|
scripts/package-release.sh devkit
|
||||||
|
scripts/package-release.sh platform # preview
|
||||||
|
scripts/package-release.sh all-in-one-demo
|
||||||
|
# enterprise → intentionally refused (future)
|
||||||
|
ls dist/ # *.tar.gz + *.sha256
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. Publish tarballs to the Gitea generic package registry
|
||||||
|
Endpoint: `PUT $GITEA/api/packages/{owner}/generic/{name}/{version}/{file}`
|
||||||
|
```bash
|
||||||
|
GITEA=http://161.33.139.73:3000; OWNER=admin; V=$(cat VERSION)
|
||||||
|
for lvl in core devkit; do
|
||||||
|
f="dist/casan-$lvl-v$V.tar.gz"
|
||||||
|
curl -fsSL -XPUT -H "Authorization: token $GITEA_TOKEN" \
|
||||||
|
--upload-file "$f" \
|
||||||
|
"$GITEA/api/packages/$OWNER/generic/casan-$lvl/$V/$(basename "$f")"
|
||||||
|
curl -fsSL -XPUT -H "Authorization: token $GITEA_TOKEN" \
|
||||||
|
--upload-file "$f.sha256" \
|
||||||
|
"$GITEA/api/packages/$OWNER/generic/casan-$lvl/$V/$(basename "$f").sha256"
|
||||||
|
done
|
||||||
|
# platform is a preview artifact:
|
||||||
|
curl -fsSL -XPUT -H "Authorization: token $GITEA_TOKEN" \
|
||||||
|
--upload-file "dist/casan-platform-preview-v$V.tar.gz" \
|
||||||
|
"$GITEA/api/packages/$OWNER/generic/casan-platform/$V-preview/casan-platform-preview-v$V.tar.gz"
|
||||||
|
```
|
||||||
|
Downstream then pulls:
|
||||||
|
```bash
|
||||||
|
curl -fsSL -H "Authorization: token $TOKEN" \
|
||||||
|
"$GITEA/api/packages/admin/generic/casan-core/1.0.0/casan-core-v1.0.0.tar.gz" -o casan-core.tar.gz
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. Publish Docker images to the Gitea container registry
|
||||||
|
```bash
|
||||||
|
V=$(cat VERSION)
|
||||||
|
docker build -f packages/casan-devkit/Dockerfile.harness -t "$REG/admin/casan-harness:$V" .
|
||||||
|
echo "$GITEA_TOKEN" | docker login "$REG" -u admin --password-stdin # REG=161.33.139.73:3000
|
||||||
|
docker push "$REG/admin/casan-harness:$V"
|
||||||
|
```
|
||||||
|
See `DOCKER_GUIDE.md` for image details. `casan-platform:$V` is preview; `casan-enterprise:$V`
|
||||||
|
is future (do not publish).
|
||||||
|
|
||||||
|
## 4. Attach bundles to a Gitea Release (optional, human-facing)
|
||||||
|
Create a tag + release via the API and upload the tarballs as release attachments:
|
||||||
|
```bash
|
||||||
|
# create release for tag vX.Y.Z, then:
|
||||||
|
curl -fsSL -XPOST -H "Authorization: token $GITEA_TOKEN" \
|
||||||
|
-F "attachment=@dist/casan-devkit-v$V.tar.gz" \
|
||||||
|
"$GITEA/api/v1/repos/admin/casan5/releases/{release_id}/assets?name=casan-devkit-v$V.tar.gz"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. Recommended cadence
|
||||||
|
- Tag `vX.Y.Z` on `main` → CI green → build bundles → publish `core` + `devkit` (always),
|
||||||
|
`platform` as `-preview`, `all-in-one-demo` for demos. Never publish `enterprise`.
|
||||||
|
- Keep `VERSION` and `packages/casan-harness/level5/harness-package.json` version in lockstep.
|
||||||
|
|
||||||
|
## Automating in CI
|
||||||
|
Add a release job to `.gitea/workflows/` that runs after the gate, calls
|
||||||
|
`scripts/package-release.sh`, and does the `curl` uploads with `${{ secrets.GITEA_TOKEN }}`.
|
||||||
|
Keep it gated on tags (`on: push: tags: ['v*']`) so ordinary pushes don't publish.
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
# CASAN harness runtime image (Level 1/2). Runs the governance gate on a mounted repo.
|
||||||
|
# Minimal: bash + python3 + openssl (+ git for repo-root marker). No app runtime.
|
||||||
|
#
|
||||||
|
# Build (from source hub or an extracted casan-devkit bundle):
|
||||||
|
# docker build -f packages/casan-devkit/Dockerfile.harness -t casan-harness:1.0.0 .
|
||||||
|
# Run the gate against a project mounted at /workspace:
|
||||||
|
# docker run --rm -v "$PWD":/workspace -w /workspace casan-harness:1.0.0 casan gate
|
||||||
|
FROM debian:bookworm-slim
|
||||||
|
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y --no-install-recommends bash python3 openssl git ca-certificates rsync \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Ship the core harness + CLI inside the image.
|
||||||
|
WORKDIR /opt/casan
|
||||||
|
COPY packages/casan-harness/ ./packages/casan-harness/
|
||||||
|
COPY bin/casan ./bin/casan
|
||||||
|
COPY VERSION ./VERSION
|
||||||
|
RUN chmod +x ./bin/casan && ln -s /opt/casan/bin/casan /usr/local/bin/casan
|
||||||
|
|
||||||
|
# Default working dir is the mounted project.
|
||||||
|
WORKDIR /workspace
|
||||||
|
ENV CASAN_HARNESS_ROOT=/opt/casan/packages/casan-harness
|
||||||
|
ENTRYPOINT ["casan"]
|
||||||
|
CMD ["help"]
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# CASAN DevKit (Level 2 — Adoption Kit)
|
||||||
|
|
||||||
|
Everything a new project needs to adopt the CASAN governance harness in a repeatable way.
|
||||||
|
**Level 2 = Level 1 core harness + adoption tooling.**
|
||||||
|
|
||||||
|
## Contents
|
||||||
|
| Path | Purpose |
|
||||||
|
|---|---|
|
||||||
|
| `install.sh` | Install core harness + `bin/casan` into a target repo, scaffold a domain, register it |
|
||||||
|
| `Dockerfile.harness` | Minimal image to run the gate on any mounted repo (`casan gate`) |
|
||||||
|
| `templates/domain-pack/` | Per-project domain scaffold (input / golden-runs / corpus / `domain-pack.yaml`) |
|
||||||
|
| `templates/gitea-workflow/ci.yml` | Reusable Gitea Actions gate workflow |
|
||||||
|
| `templates/project/` | Minimal new-project skeleton that consumes the harness |
|
||||||
|
|
||||||
|
## Quick adopt
|
||||||
|
```bash
|
||||||
|
# from a CASAN source hub or an extracted casan-devkit bundle
|
||||||
|
packages/casan-devkit/install.sh --target ../my-project --project ticketing --domain "Ticketing"
|
||||||
|
cd ../my-project
|
||||||
|
# add requirement + golden baseline under apps/ticketing/domain/, then:
|
||||||
|
CASAN_DOMAIN_ROOT=apps/ticketing/domain bin/casan gate
|
||||||
|
bin/casan reuse # HARNESS_REUSE_VALID
|
||||||
|
```
|
||||||
|
|
||||||
|
## Guides
|
||||||
|
- `docs/packaging/ADOPTION_GUIDE.md` — end-to-end adoption
|
||||||
|
- `docs/packaging/DOMAIN_PACK_GUIDE.md` — how to fill a domain pack
|
||||||
|
- `docs/packaging/CI_GUIDE.md` — wire the gate into Gitea CI
|
||||||
|
- `docs/packaging/DOCKER_GUIDE.md` — run/build the harness image
|
||||||
|
|
||||||
|
Adoption is **config + domain only** — you never edit gate logic (H1→H7).
|
||||||
Executable
+72
@@ -0,0 +1,72 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# CASAN DevKit installer — adopt the CASAN harness into a target project.
|
||||||
|
#
|
||||||
|
# Copies the Level-1 core harness (packages/casan-harness + bin/casan) into a target
|
||||||
|
# repo, scaffolds a per-project domain from the domain-pack template, and registers the
|
||||||
|
# project in project-registry.json so `casan reuse` sees it. Does NOT touch harness gate
|
||||||
|
# logic — adoption is config + domain only.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# packages/casan-devkit/install.sh --target <dir> --project <id> [--domain <name>]
|
||||||
|
#
|
||||||
|
# Run from a CASAN source hub (or an extracted casan-devkit bundle).
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
TARGET="" PROJECT="" DOMAIN="custom"
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--target) TARGET="$2"; shift 2 ;;
|
||||||
|
--project) PROJECT="$2"; shift 2 ;;
|
||||||
|
--domain) DOMAIN="$2"; shift 2 ;;
|
||||||
|
-h|--help) grep '^#' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
|
||||||
|
*) echo "install: unknown arg $1" >&2; exit 64 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
[[ -n "$TARGET" && -n "$PROJECT" ]] || { echo "install: --target and --project are required" >&2; exit 64; }
|
||||||
|
|
||||||
|
SRC="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" # source-hub / bundle root
|
||||||
|
[[ -d "$SRC/packages/casan-harness" ]] || { echo "install: cannot find packages/casan-harness under $SRC" >&2; exit 1; }
|
||||||
|
|
||||||
|
echo "==> installing CASAN core into $TARGET (project=$PROJECT domain=$DOMAIN)"
|
||||||
|
mkdir -p "$TARGET/packages" "$TARGET/bin" "$TARGET/apps/$PROJECT/domain"
|
||||||
|
|
||||||
|
# 1) core harness + CLI
|
||||||
|
rsync -a --exclude='__pycache__' --exclude='*.pyc' "$SRC/packages/casan-harness/" "$TARGET/packages/casan-harness/"
|
||||||
|
cp "$SRC/bin/casan" "$TARGET/bin/casan"; chmod +x "$TARGET/bin/casan"
|
||||||
|
[[ -f "$SRC/VERSION" ]] && cp "$SRC/VERSION" "$TARGET/VERSION"
|
||||||
|
|
||||||
|
# 2) per-project domain from the domain-pack template
|
||||||
|
rsync -a "$SRC/packages/casan-devkit/templates/domain-pack/" "$TARGET/apps/$PROJECT/domain/"
|
||||||
|
|
||||||
|
# 3) Gitea CI workflow (adoption)
|
||||||
|
mkdir -p "$TARGET/.gitea/workflows"
|
||||||
|
cp "$SRC/packages/casan-devkit/templates/gitea-workflow/ci.yml" "$TARGET/.gitea/workflows/casan-ci.yml"
|
||||||
|
|
||||||
|
# 4) register in project-registry.json (append if absent)
|
||||||
|
REG="$TARGET/packages/casan-harness/level5/project-registry.json"
|
||||||
|
python3 - "$REG" "$PROJECT" "$DOMAIN" <<'PY'
|
||||||
|
import json, sys
|
||||||
|
reg, pid, dom = sys.argv[1], sys.argv[2], sys.argv[3]
|
||||||
|
data = json.load(open(reg))
|
||||||
|
if not any(p.get("project_id") == pid for p in data["projects"]):
|
||||||
|
data["projects"].append({
|
||||||
|
"project_id": pid, "domain": dom, "domain_root": f"apps/{pid}/domain",
|
||||||
|
"harness_package": "fpt-casan-sdd-harness",
|
||||||
|
"harness_version": data["projects"][0]["harness_version"],
|
||||||
|
"status": "active",
|
||||||
|
})
|
||||||
|
json.dump(data, open(reg, "w"), indent=2, ensure_ascii=False); open(reg, "a").write("\n")
|
||||||
|
print(f"registered {pid}")
|
||||||
|
else:
|
||||||
|
print(f"{pid} already registered")
|
||||||
|
PY
|
||||||
|
|
||||||
|
cat <<EOF
|
||||||
|
|
||||||
|
==> done. Next steps in $TARGET:
|
||||||
|
1. Put your requirement in apps/$PROJECT/domain/input/requirement.md
|
||||||
|
2. Add golden baseline in apps/$PROJECT/domain/golden-runs/
|
||||||
|
3. Run the gate: CASAN_DOMAIN_ROOT=apps/$PROJECT/domain bin/casan gate
|
||||||
|
4. Prove reuse: bin/casan reuse # expects HARNESS_REUSE_VALID
|
||||||
|
See docs/packaging/ADOPTION_GUIDE.md and DOMAIN_PACK_GUIDE.md.
|
||||||
|
EOF
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
# Benign corpus for H4 false-positive budget. One .txt per benign sample.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"note":"red-team attack vectors for this domain — one JSON object per line"}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"note":"red-team vectors used by benign-fp-report / redteam metrics"}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# CASAN Domain Pack — declarative domain adoption (template).
|
||||||
|
# Copy this into apps/<project>/domain/ and fill it in. The harness reads domain data
|
||||||
|
# from CASAN_DOMAIN_ROOT (defaults to apps/<project>/domain); this file documents what
|
||||||
|
# each project must provide. See docs/packaging/DOMAIN_PACK_GUIDE.md.
|
||||||
|
|
||||||
|
domain:
|
||||||
|
id: custom # short id, e.g. okr, ticketing, inventory
|
||||||
|
name: "Custom domain"
|
||||||
|
owner: your-team
|
||||||
|
|
||||||
|
# Requirement/architecture the harness pipeline consumes (H3/traceability, source-gen).
|
||||||
|
input:
|
||||||
|
requirement: input/requirement.md # FR-xx table drives traceability
|
||||||
|
architecture: input/architecture.md # optional tech-stack/context
|
||||||
|
|
||||||
|
# Golden baselines for drift detection (H7). similarity=1.0 vs golden ⇒ no drift.
|
||||||
|
golden_runs:
|
||||||
|
dir: golden-runs
|
||||||
|
# - artifact: plan -> golden-runs/plan.golden.txt
|
||||||
|
|
||||||
|
# Red-team + benign corpus for H4 security scoring (attack recall + FP budget).
|
||||||
|
corpus:
|
||||||
|
redteam: corpus/redteam-corpus.jsonl # attack vectors (per-line JSON)
|
||||||
|
redteam_vectors: corpus/redteam-vectors.jsonl
|
||||||
|
benign: corpus/benign-corpus # dir of benign .txt (false-positive budget)
|
||||||
|
|
||||||
|
# Requirement→code→test map for the traceability gate (Plan-10).
|
||||||
|
traceability_map: traceability-map.json
|
||||||
|
|
||||||
|
# Optional per-domain threshold overrides (else harness defaults apply).
|
||||||
|
thresholds:
|
||||||
|
# drift_min_similarity: 0.85
|
||||||
|
# fp_rate_max: 0.05
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
# Golden baselines for drift-detect (H7). Add <artifact>.golden.txt here.
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# <Project> Requirement (template)
|
||||||
|
|
||||||
|
> Replace this with your domain's requirements. The **FR-xx table below drives the
|
||||||
|
> traceability gate** (Plan-10): every `FR-xx` must map to ≥1 code file + ≥1 test in
|
||||||
|
> `traceability-map.json`. Keep the `| FR-xx | ... |` table format.
|
||||||
|
|
||||||
|
## Functional Requirements
|
||||||
|
|
||||||
|
| ID | Requirement |
|
||||||
|
|------|-------------|
|
||||||
|
| FR-01 | Example: user can log in and receive a session token |
|
||||||
|
| FR-02 | Example: user can create a primary domain entity |
|
||||||
|
| FR-03 | Example: user can update entity progress |
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
- Add use cases, constraints, and UI expectations as normal prose below.
|
||||||
|
- Secrets/credentials must NOT appear here (H4 input scan will block them).
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# CASAN harness gate — Gitea Actions workflow (adoption template).
|
||||||
|
# Copy to .gitea/workflows/casan-ci.yml in your project. Assumes the CASAN core harness
|
||||||
|
# lives at packages/casan-harness/ (via casan-devkit install.sh) and domain data at
|
||||||
|
# apps/<project>/domain/. Runs the full governance gate on every push/PR.
|
||||||
|
name: CASAN Gate
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
casan-gate:
|
||||||
|
runs-on: ci-runner
|
||||||
|
timeout-minutes: 45
|
||||||
|
env:
|
||||||
|
CASAN_CI_RUN_FRONTEND: "0" # set 1 if your project has a frontend workspace
|
||||||
|
CASAN_CI_RUN_BACKEND: "0" # set 1 if your project has backend tests
|
||||||
|
CASAN_CI_RUN_INFRA_LAB: "0"
|
||||||
|
CASAN_CI_STEP_TIMEOUT_SEC: "1200" # headroom; some suites are model-backed
|
||||||
|
# CASAN_DOMAIN_ROOT: apps/<project>/domain # uncomment + set for your project
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Ensure toolchain
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
command -v python3 >/dev/null || { apt-get update && apt-get install -y python3; }
|
||||||
|
python3 --version
|
||||||
|
|
||||||
|
- name: Run CASAN harness gate
|
||||||
|
run: bash packages/casan-harness/scripts/bash/ci-harness-gate.sh
|
||||||
|
|
||||||
|
- name: Verify audit chain + policy bundle
|
||||||
|
run: |
|
||||||
|
bash packages/casan-harness/scripts/bash/verify-audit-chain.sh
|
||||||
|
bash packages/casan-harness/scripts/bash/sign-policy-bundle.sh verify
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# CASAN harness gate — Gitea Actions workflow (adoption template).
|
||||||
|
# Copy to .gitea/workflows/casan-ci.yml in your project. Assumes the CASAN core harness
|
||||||
|
# lives at packages/casan-harness/ (via casan-devkit install.sh) and domain data at
|
||||||
|
# apps/<project>/domain/. Runs the full governance gate on every push/PR.
|
||||||
|
name: CASAN Gate
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
casan-gate:
|
||||||
|
runs-on: ci-runner
|
||||||
|
timeout-minutes: 45
|
||||||
|
env:
|
||||||
|
CASAN_CI_RUN_FRONTEND: "0" # set 1 if your project has a frontend workspace
|
||||||
|
CASAN_CI_RUN_BACKEND: "0" # set 1 if your project has backend tests
|
||||||
|
CASAN_CI_RUN_INFRA_LAB: "0"
|
||||||
|
CASAN_CI_STEP_TIMEOUT_SEC: "1200" # headroom; some suites are model-backed
|
||||||
|
# CASAN_DOMAIN_ROOT: apps/<project>/domain # uncomment + set for your project
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Ensure toolchain
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
command -v python3 >/dev/null || { apt-get update && apt-get install -y python3; }
|
||||||
|
python3 --version
|
||||||
|
|
||||||
|
- name: Run CASAN harness gate
|
||||||
|
run: bash packages/casan-harness/scripts/bash/ci-harness-gate.sh
|
||||||
|
|
||||||
|
- name: Verify audit chain + policy bundle
|
||||||
|
run: |
|
||||||
|
bash packages/casan-harness/scripts/bash/verify-audit-chain.sh
|
||||||
|
bash packages/casan-harness/scripts/bash/sign-policy-bundle.sh verify
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# <My Project> — CASAN-governed project (scaffold)
|
||||||
|
|
||||||
|
Generated by `casan-devkit install.sh`. Layout:
|
||||||
|
|
||||||
|
```
|
||||||
|
packages/casan-harness/ # Level-1 core harness (installed; do not edit gate logic)
|
||||||
|
bin/casan # CLI wrapper
|
||||||
|
apps/<project>/domain/ # YOUR domain pack (input / golden-runs / corpus / domain-pack.yaml)
|
||||||
|
.gitea/workflows/casan-ci.yml # gate on push/PR
|
||||||
|
.specify/ # runtime state (created on first run: logs, audit, governance)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Run
|
||||||
|
```bash
|
||||||
|
CASAN_DOMAIN_ROOT=apps/<project>/domain bin/casan gate # full governance gate
|
||||||
|
bin/casan run in.txt out.txt my_step -- <command> # one governed step
|
||||||
|
bin/casan reuse # HARNESS_REUSE_VALID
|
||||||
|
```
|
||||||
|
|
||||||
|
Fill `apps/<project>/domain/` first (see DOMAIN_PACK_GUIDE.md). Upgrade the harness by
|
||||||
|
re-running install.sh with a newer casan-devkit — your domain data is untouched.
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# CASAN Enterprise / Governed Agent Console (Level 4) · **FUTURE / structure-only**
|
||||||
|
|
||||||
|
> Status: **FUTURE.** Documented + scaffolded, NOT implemented in this task.
|
||||||
|
> `package-release.sh enterprise` **refuses to build** (no fake-complete package) — several
|
||||||
|
> building blocks already exist inside the core harness, but the governed console itself
|
||||||
|
> does not. Do not ship this as a product yet.
|
||||||
|
|
||||||
|
Enterprise governed AI-SDLC console. Packages: `casan-enterprise`, `casan-governed-console`.
|
||||||
|
|
||||||
|
## Building blocks that ALREADY exist (in core harness, reusable)
|
||||||
|
| Capability | Where |
|
||||||
|
|---|---|
|
||||||
|
| RBAC | `packages/casan-harness/scripts/bash/rbac-check.py` |
|
||||||
|
| Tenant isolation | `tenant-store.sh`, `tenant-paths.sh`, `tenant-registry-verify.sh`, `tenant-crypt.sh` |
|
||||||
|
| Approval workflow | `approval-verify.sh`, `approval-sign.sh`, `approval-jwt-mint.py` |
|
||||||
|
| KMS | `vault-kms.sh` |
|
||||||
|
| WORM ledger | `worm-ledger.py` |
|
||||||
|
| Kill-switch / quota | `kill-switch.sh`, `cost-spike-detect.sh` |
|
||||||
|
| Loop governance | Plan-17 loop primitives (`loop-*.py`) |
|
||||||
|
|
||||||
|
## Components still to build (NOT in this task)
|
||||||
|
- Governed Chat Console (Plan-18) · Prompt Mode Router · Model Provider Management
|
||||||
|
- Operator mode · Codegen mode · Agent/Skill Registry · policy-versioning UI
|
||||||
|
|
||||||
|
## Why it refuses to package
|
||||||
|
Per the packaging principle, a level that isn't implemented must **fail clearly** rather
|
||||||
|
than emit a fake-complete artifact. Enterprise is `status: future` in
|
||||||
|
`packaging/levels.json`, so `scripts/package-release.sh enterprise` exits non-zero with an
|
||||||
|
explanation. When the console is built, flip its status to `preview`/`implemented`.
|
||||||
|
|
||||||
|
## To implement later
|
||||||
|
Sequence: Plan-14 (RBAC console) → Plan-13 (Control Plane) → Plan-18 (Governed Chat
|
||||||
|
Console: read-only → operator → chat-as-loop → multi-tenant). Reuse the existing blocks
|
||||||
|
above instead of re-writing them.
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# CASAN Platform (Level 3 — Productization UI) · **PREVIEW / structure-only**
|
||||||
|
|
||||||
|
> Status: **PREVIEW.** Only the AgentOps **dashboard** exists today (shipped inside the core
|
||||||
|
> harness). The rest of the platform UI is scaffolded here as structure + intent — NOT
|
||||||
|
> implemented in this task. `package-release.sh platform` builds a clearly-stamped
|
||||||
|
> `casan-platform-preview-*` bundle containing only what exists.
|
||||||
|
|
||||||
|
Optional layer for teams that want UI / dashboard / visibility. Packages: `casan-platform`,
|
||||||
|
`casan-control-panel`.
|
||||||
|
|
||||||
|
## Components
|
||||||
|
| Component | Status | Where |
|
||||||
|
|---|---|---|
|
||||||
|
| AgentOps Dashboard | ✅ exists | `packages/casan-harness/scripts/bash/dashboard-server.py` + `dashboard-serve.sh` (`casan dashboard`) |
|
||||||
|
| Control Panel | 📋 planned | Plan-13 (`docs/plans/CASAN_PLAN_13_CONTROL_PLANE.md`) |
|
||||||
|
| Evidence Pack Viewer | 📋 planned | reads `docs/output/casan/evidence-packs/` |
|
||||||
|
| Attack Battery Viewer | 📋 planned | reads red-team corpus + H4 recall results |
|
||||||
|
| Run History Viewer | 📋 planned | reads `.specify/logs/level5/pipeline-run.jsonl` |
|
||||||
|
| Read-only Ask CASAN | 📋 planned | Plan-18 MVP-0 (read-only) |
|
||||||
|
| Gitea webhook integration | 📋 planned | trigger gate / publish evidence on push |
|
||||||
|
|
||||||
|
## Build (preview)
|
||||||
|
```bash
|
||||||
|
scripts/package-release.sh platform # → dist/casan-platform-preview-vX.Y.Z.tar.gz
|
||||||
|
```
|
||||||
|
The bundle includes a `PREVIEW-INCOMPLETE.txt` marker. Do not treat it as a finished product.
|
||||||
|
|
||||||
|
## To implement later
|
||||||
|
Start from Plan-13 (Control Plane) + Plan-18 MVP-0 (Ask CASAN read-only). Keep the UI
|
||||||
|
**read-only over harness artifacts** first; write/governed actions belong to Level 4.
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
{
|
||||||
|
"casan_packaging": "level-based source hub",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"note": "Single source of truth for what each release bundle contains and its maturity. The repo is a source hub holding all levels; releases are SPLIT so downstream adopts only the level it needs. See docs/packaging/CASAN_PACKAGING_PLAN.md.",
|
||||||
|
"levels": {
|
||||||
|
"core": {
|
||||||
|
"level": 1,
|
||||||
|
"package_names": ["casan-core", "casan-harness"],
|
||||||
|
"status": "implemented",
|
||||||
|
"summary": "H1-H7 harness core, security/action gates, evidence pack, audit, cost/telemetry, hardening tests, policy/config defaults, lightweight CLI.",
|
||||||
|
"target_users": ["governance-harness-only projects", "BJT initial adoption", "CI gate usage"],
|
||||||
|
"includes": [
|
||||||
|
"packages/casan-harness/scripts",
|
||||||
|
"packages/casan-harness/security",
|
||||||
|
"packages/casan-harness/governance",
|
||||||
|
"packages/casan-harness/agentops",
|
||||||
|
"packages/casan-harness/config",
|
||||||
|
"packages/casan-harness/level5",
|
||||||
|
"packages/casan-harness/memory",
|
||||||
|
"packages/casan-harness/templates",
|
||||||
|
"packages/casan-harness/tests",
|
||||||
|
"packages/casan-harness/init-options.json",
|
||||||
|
"packages/casan-harness/README.md",
|
||||||
|
"bin/casan",
|
||||||
|
"VERSION"
|
||||||
|
],
|
||||||
|
"excludes_globs": ["**/__pycache__/**", "**/*.pyc", "**/.DS_Store"],
|
||||||
|
"artifact": "casan-core-v{VERSION}.tar.gz",
|
||||||
|
"docker_image": "casan-harness:{VERSION}"
|
||||||
|
},
|
||||||
|
"devkit": {
|
||||||
|
"level": 2,
|
||||||
|
"package_names": ["casan-devkit", "casan-project-kit"],
|
||||||
|
"status": "implemented",
|
||||||
|
"summary": "Level 1 + project templates, domain-pack scaffold, Gitea workflow templates, harness Dockerfile, install script, adoption/CI/domain-pack guides.",
|
||||||
|
"target_users": ["new projects adopting CASAN", "internal teams needing a repeatable setup"],
|
||||||
|
"extends": "core",
|
||||||
|
"includes": [
|
||||||
|
"packages/casan-devkit",
|
||||||
|
"docs/packaging/ADOPTION_GUIDE.md",
|
||||||
|
"docs/packaging/CI_GUIDE.md",
|
||||||
|
"docs/packaging/DOMAIN_PACK_GUIDE.md",
|
||||||
|
"docs/packaging/DOCKER_GUIDE.md",
|
||||||
|
"docs/packaging/CASAN_PACKAGING_PLAN.md",
|
||||||
|
"scripts/package-release.sh"
|
||||||
|
],
|
||||||
|
"artifact": "casan-devkit-v{VERSION}.tar.gz"
|
||||||
|
},
|
||||||
|
"platform": {
|
||||||
|
"level": 3,
|
||||||
|
"package_names": ["casan-platform", "casan-control-panel"],
|
||||||
|
"status": "preview",
|
||||||
|
"summary": "Optional productization UI: Control Panel, Dashboard, Evidence/Attack/Run-History viewers, read-only Ask CASAN, Gitea webhook. Only the AgentOps dashboard exists today; the rest is structure-only.",
|
||||||
|
"target_users": ["teams wanting UI/dashboard/visibility", "demo/training/productization"],
|
||||||
|
"extends": "devkit",
|
||||||
|
"implemented_components": ["agentops-dashboard (packages/casan-harness/scripts/bash/dashboard-server.py + dashboard-serve.sh)"],
|
||||||
|
"pending_components": ["control-panel", "evidence-pack-viewer", "attack-battery-viewer", "run-history-viewer", "read-only Ask CASAN", "gitea-webhook-integration"],
|
||||||
|
"includes": [
|
||||||
|
"packages/casan-platform",
|
||||||
|
"packages/casan-harness/scripts/bash/dashboard-server.py",
|
||||||
|
"packages/casan-harness/scripts/bash/dashboard-serve.sh"
|
||||||
|
],
|
||||||
|
"artifact": "casan-platform-preview-v{VERSION}.tar.gz",
|
||||||
|
"docker_image": "casan-platform:{VERSION} (preview)"
|
||||||
|
},
|
||||||
|
"enterprise": {
|
||||||
|
"level": 4,
|
||||||
|
"package_names": ["casan-enterprise", "casan-governed-console"],
|
||||||
|
"status": "future",
|
||||||
|
"summary": "Governed Chat Console, Prompt Mode Router, Model Provider Management, Operator/Codegen modes, Agent/Skill Registry, RBAC, approval workflow, tenant isolation, KMS/WORM, policy versioning.",
|
||||||
|
"target_users": ["enterprise/internal platform", "multi-project governance", "official governed AI-SDLC console"],
|
||||||
|
"extends": "platform",
|
||||||
|
"implemented_components": [
|
||||||
|
"RBAC (rbac-check.py)",
|
||||||
|
"tenant isolation (tenant-store.sh/tenant-paths.sh/tenant-registry-verify.sh/tenant-crypt.sh)",
|
||||||
|
"approval workflow (approval-verify.sh/approval-sign.sh/approval-jwt-mint.py)",
|
||||||
|
"KMS (vault-kms.sh)",
|
||||||
|
"WORM (worm-ledger.py)",
|
||||||
|
"kill-switch (kill-switch.sh)"
|
||||||
|
],
|
||||||
|
"pending_components": ["governed-chat-console", "prompt-mode-router", "model-provider-management", "operator-mode", "codegen-mode", "agent-skill-registry", "policy-versioning-ui"],
|
||||||
|
"includes": ["packages/casan-enterprise"],
|
||||||
|
"artifact": "casan-enterprise-preview-v{VERSION}.tar.gz",
|
||||||
|
"docker_image": "casan-enterprise:{VERSION} (future)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bundles": {
|
||||||
|
"core": {"builds": ["core"], "status": "implemented"},
|
||||||
|
"devkit": {"builds": ["core", "devkit"], "status": "implemented"},
|
||||||
|
"platform": {"builds": ["core", "devkit", "platform"], "status": "preview"},
|
||||||
|
"all-in-one-demo": {"builds": ["core", "devkit", "platform", "apps/okr/domain", "backend", "frontend", "docs/output/casan", "package.json", "package-lock.json", "Dockerfile.backend", "Dockerfile.frontend", "docker-compose.prod.yml"], "status": "implemented", "note": "Full runnable snapshot: harness + OKR app (backend/frontend) + OKR demo domain + latest evidence, for demo/training. The traceability gate needs the app source, so it is bundled here."},
|
||||||
|
"enterprise": {"builds": ["enterprise"], "status": "future", "note": "Refused by package-release.sh — Level 4 is structure-only; no fake-complete artifact."}
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable
+149
@@ -0,0 +1,149 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# CASAN level-based release packager.
|
||||||
|
#
|
||||||
|
# scripts/package-release.sh core # Level 1 — always buildable
|
||||||
|
# scripts/package-release.sh devkit # Level 2 — always buildable
|
||||||
|
# scripts/package-release.sh platform # Level 3 — PREVIEW (dashboard only today)
|
||||||
|
# scripts/package-release.sh all-in-one-demo # everything runnable + OKR demo domain
|
||||||
|
# scripts/package-release.sh enterprise # Level 4 — FUTURE → fails clearly
|
||||||
|
#
|
||||||
|
# Contents + maturity are driven by packaging/levels.json (single source of truth).
|
||||||
|
# A bundle whose level is `future` is REFUSED (no fake-complete package). A `preview`
|
||||||
|
# bundle is built but clearly stamped PREVIEW/INCOMPLETE and named *-preview.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
cd "$ROOT"
|
||||||
|
MANIFEST="packaging/levels.json"
|
||||||
|
VERSION="$(cat VERSION 2>/dev/null || echo 0.0.0)"
|
||||||
|
DIST="$ROOT/dist"
|
||||||
|
|
||||||
|
BUNDLE="${1:-}"
|
||||||
|
if [[ -z "$BUNDLE" ]]; then
|
||||||
|
echo "Usage: package-release.sh <core|devkit|platform|all-in-one-demo|enterprise>" >&2
|
||||||
|
exit 64
|
||||||
|
fi
|
||||||
|
[[ -f "$MANIFEST" ]] || { echo "package-release: missing $MANIFEST" >&2; exit 1; }
|
||||||
|
|
||||||
|
# --- read bundle definition from the manifest via python3 ---------------------
|
||||||
|
read -r BUNDLE_STATUS BUILDS < <(python3 - "$MANIFEST" "$BUNDLE" <<'PY'
|
||||||
|
import json, sys
|
||||||
|
m = json.load(open(sys.argv[1])); b = sys.argv[2]
|
||||||
|
bundles = m["bundles"]
|
||||||
|
if b not in bundles:
|
||||||
|
print("UNKNOWN -"); sys.exit(0)
|
||||||
|
spec = bundles[b]
|
||||||
|
print(spec.get("status", "unknown"), ",".join(spec.get("builds", [])))
|
||||||
|
PY
|
||||||
|
)
|
||||||
|
|
||||||
|
if [[ "$BUNDLE_STATUS" == "UNKNOWN" ]]; then
|
||||||
|
echo "package-release: unknown bundle '$BUNDLE'. Valid: core devkit platform all-in-one-demo enterprise" >&2
|
||||||
|
exit 64
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- refuse to build a fake-complete package for a not-implemented level -------
|
||||||
|
if [[ "$BUNDLE_STATUS" == "future" ]]; then
|
||||||
|
cat >&2 <<EOF
|
||||||
|
package-release: '$BUNDLE' is a FUTURE / structure-only level — refusing to build.
|
||||||
|
|
||||||
|
Level 4 (enterprise / governed console) is documented + scaffolded but NOT implemented
|
||||||
|
in this task. Some building blocks already exist inside core (RBAC, tenant isolation,
|
||||||
|
approval workflow, KMS, WORM) — see packages/casan-enterprise/README.md — but the
|
||||||
|
Governed Chat Console / Prompt Router / Model-Provider Management do not exist yet, so
|
||||||
|
no 'complete' enterprise artifact can be produced. This is intentional (no fake-complete
|
||||||
|
packages). Build 'core', 'devkit', 'platform', or 'all-in-one-demo' instead.
|
||||||
|
EOF
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
|
||||||
|
PREVIEW=0
|
||||||
|
SUFFIX=""
|
||||||
|
[[ "$BUNDLE_STATUS" == "preview" ]] && { PREVIEW=1; SUFFIX="-preview"; }
|
||||||
|
|
||||||
|
# --- gather include globs for the levels this bundle builds -------------------
|
||||||
|
gather_includes() { # <level>
|
||||||
|
python3 - "$MANIFEST" "$1" <<'PY'
|
||||||
|
import json, sys
|
||||||
|
m = json.load(open(sys.argv[1])); lvl = sys.argv[2]
|
||||||
|
# a "level" here is a levels.* key; the demo pseudo-paths are handled by the caller
|
||||||
|
node = m["levels"].get(lvl)
|
||||||
|
if node:
|
||||||
|
for inc in node.get("includes", []):
|
||||||
|
print(inc)
|
||||||
|
PY
|
||||||
|
}
|
||||||
|
|
||||||
|
STAGE="$(mktemp -d)"
|
||||||
|
PKGDIR="$STAGE/casan-$BUNDLE$SUFFIX-v$VERSION"
|
||||||
|
mkdir -p "$PKGDIR"
|
||||||
|
trap 'rm -rf "$STAGE"' EXIT
|
||||||
|
|
||||||
|
copy_path() { # <relpath>
|
||||||
|
local src="$ROOT/$1"
|
||||||
|
if [[ ! -e "$src" ]]; then
|
||||||
|
echo " ! skip (missing): $1" >&2
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
local dest="$PKGDIR/$1"
|
||||||
|
mkdir -p "$(dirname "$dest")"
|
||||||
|
# copy, dropping caches/artifacts
|
||||||
|
if [[ -d "$src" ]]; then
|
||||||
|
rsync -a --exclude='__pycache__' --exclude='*.pyc' --exclude='.DS_Store' "$src/" "$dest/" 2>/dev/null \
|
||||||
|
|| cp -R "$src/" "$dest/"
|
||||||
|
else
|
||||||
|
cp "$src" "$dest"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "==> building bundle '$BUNDLE' (status=$BUNDLE_STATUS) version=$VERSION"
|
||||||
|
SEEN=" " # space-delimited dedup (bash 3.2 compatible — no associative arrays)
|
||||||
|
seen() { case "$SEEN" in *" $1 "*) return 0;; *) SEEN="$SEEN$1 "; return 1;; esac; }
|
||||||
|
IFS=',' read -ra LEVELS <<< "$BUILDS"
|
||||||
|
for lvl in "${LEVELS[@]}"; do
|
||||||
|
# Entries that are level keys (core/devkit/platform/enterprise) expand via the manifest;
|
||||||
|
# anything else is a literal path (demo snapshot: backend, frontend, apps/okr/domain, ...).
|
||||||
|
case "$lvl" in
|
||||||
|
core|devkit|platform|enterprise) ;;
|
||||||
|
*) seen "$lvl" && continue
|
||||||
|
echo " + $lvl"; copy_path "$lvl"; continue ;;
|
||||||
|
esac
|
||||||
|
echo " level: $lvl"
|
||||||
|
while IFS= read -r inc; do
|
||||||
|
[[ -z "$inc" ]] && continue
|
||||||
|
seen "$inc" && continue
|
||||||
|
echo " + $inc"; copy_path "$inc"
|
||||||
|
done < <(gather_includes "$lvl")
|
||||||
|
done
|
||||||
|
|
||||||
|
# --- bundle manifest + preview notice ----------------------------------------
|
||||||
|
{
|
||||||
|
echo "CASAN release bundle"
|
||||||
|
echo "bundle: $BUNDLE"
|
||||||
|
echo "version: $VERSION"
|
||||||
|
echo "status: $BUNDLE_STATUS"
|
||||||
|
echo "built: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||||
|
echo "builds: $BUILDS"
|
||||||
|
} > "$PKGDIR/BUNDLE-MANIFEST.txt"
|
||||||
|
|
||||||
|
if [[ "$PREVIEW" == 1 ]]; then
|
||||||
|
cat > "$PKGDIR/PREVIEW-INCOMPLETE.txt" <<EOF
|
||||||
|
⚠️ PREVIEW / INCOMPLETE BUNDLE — NOT production-complete.
|
||||||
|
|
||||||
|
This '$BUNDLE' bundle is a PREVIEW. Only the components that exist today are included
|
||||||
|
(see packaging/levels.json -> levels.$BUNDLE.implemented_components). Pending components
|
||||||
|
are listed under pending_components. Do not treat this as a finished product.
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- checksums + tar ----------------------------------------------------------
|
||||||
|
( cd "$PKGDIR" && find . -type f ! -name SHA256SUMS -print0 | sort -z | xargs -0 shasum -a 256 > SHA256SUMS 2>/dev/null || true )
|
||||||
|
mkdir -p "$DIST"
|
||||||
|
ARTIFACT="casan-$BUNDLE$SUFFIX-v$VERSION.tar.gz"
|
||||||
|
tar -czf "$DIST/$ARTIFACT" -C "$STAGE" "casan-$BUNDLE$SUFFIX-v$VERSION"
|
||||||
|
SIZE="$(du -h "$DIST/$ARTIFACT" | cut -f1)"
|
||||||
|
( cd "$DIST" && shasum -a 256 "$ARTIFACT" > "$ARTIFACT.sha256" )
|
||||||
|
|
||||||
|
echo "==> BUILT $DIST/$ARTIFACT ($SIZE)"
|
||||||
|
[[ "$PREVIEW" == 1 ]] && echo " (PREVIEW — incomplete; see PREVIEW-INCOMPLETE.txt inside)"
|
||||||
|
echo " sha256: $(cut -d' ' -f1 "$DIST/$ARTIFACT.sha256")"
|
||||||
Reference in New Issue
Block a user