nccgs 1.1.1 → 1.3.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/.claude/agents/nccgs-adversarial-reviewer.md +7 -1
- package/.claude/agents/nccgs-engine-programmer.md +2 -1
- package/.claude/agents/nccgs-gameplay-programmer.md +2 -1
- package/.claude/agents/nccgs-qa-engineer.md +2 -1
- package/.claude/agents/nccgs-ui-programmer.md +2 -1
- package/.claude/agents/nccgs-unity-build-specialist.md +2 -1
- package/.claude/agents/nccgs-unity-implementer.md +2 -1
- package/.claude/agents/nccgs-unity-systems-specialist.md +2 -1
- package/.claude/agents/nccgs-unity-ui-specialist.md +2 -1
- package/.claude/agents/nccgs-verification-engineer.md +2 -1
- package/.claude/nccgs/VERSION +1 -1
- package/.claude/{agents → nccgs/agent-library}/nccgs-accessibility-specialist.md +0 -1
- package/.claude/nccgs/agent-library/nccgs-adversarial-reviewer.md +30 -0
- package/.claude/{agents → nccgs/agent-library}/nccgs-ai-programmer.md +0 -1
- package/.claude/{agents → nccgs/agent-library}/nccgs-analytics-engineer.md +0 -1
- package/.claude/{agents → nccgs/agent-library}/nccgs-art-direction-lead.md +0 -1
- package/.claude/{agents → nccgs/agent-library}/nccgs-audio-direction-lead.md +0 -1
- package/.claude/{agents → nccgs/agent-library}/nccgs-creative-director.md +0 -1
- package/.claude/{agents → nccgs/agent-library}/nccgs-documentation-manager.md +0 -1
- package/.claude/{agents → nccgs/agent-library}/nccgs-economy-designer.md +0 -1
- package/.claude/nccgs/agent-library/nccgs-engine-programmer.md +25 -0
- package/.claude/{agents → nccgs/agent-library}/nccgs-game-design-lead.md +0 -1
- package/.claude/{agents → nccgs/agent-library}/nccgs-game-designer.md +0 -1
- package/.claude/nccgs/agent-library/nccgs-gameplay-programmer.md +25 -0
- package/.claude/{agents → nccgs/agent-library}/nccgs-level-designer.md +0 -1
- package/.claude/{agents → nccgs/agent-library}/nccgs-live-ops-designer.md +0 -1
- package/.claude/{agents → nccgs/agent-library}/nccgs-localization-lead.md +0 -1
- package/.claude/{agents → nccgs/agent-library}/nccgs-narrative-lead.md +0 -1
- package/.claude/{agents → nccgs/agent-library}/nccgs-network-programmer.md +0 -1
- package/.claude/{agents → nccgs/agent-library}/nccgs-performance-analyst.md +0 -1
- package/.claude/{agents → nccgs/agent-library}/nccgs-producer.md +0 -1
- package/.claude/{agents → nccgs/agent-library}/nccgs-production-coordinator.md +0 -1
- package/.claude/{agents → nccgs/agent-library}/nccgs-programming-lead.md +0 -1
- package/.claude/{agents → nccgs/agent-library}/nccgs-prototyper.md +0 -1
- package/.claude/nccgs/agent-library/nccgs-qa-engineer.md +25 -0
- package/.claude/{agents → nccgs/agent-library}/nccgs-qa-lead.md +0 -1
- package/.claude/{agents → nccgs/agent-library}/nccgs-release-engineer.md +0 -1
- package/.claude/{agents → nccgs/agent-library}/nccgs-release-lead.md +0 -1
- package/.claude/{agents → nccgs/agent-library}/nccgs-security-engineer.md +0 -1
- package/.claude/{agents → nccgs/agent-library}/nccgs-sound-designer.md +0 -1
- package/.claude/{agents → nccgs/agent-library}/nccgs-systems-designer.md +0 -1
- package/.claude/{agents → nccgs/agent-library}/nccgs-technical-architect.md +0 -1
- package/.claude/{agents → nccgs/agent-library}/nccgs-technical-artist.md +0 -1
- package/.claude/{agents → nccgs/agent-library}/nccgs-technical-director.md +0 -1
- package/.claude/{agents → nccgs/agent-library}/nccgs-tools-programmer.md +0 -1
- package/.claude/nccgs/agent-library/nccgs-ui-programmer.md +25 -0
- package/.claude/nccgs/agent-library/nccgs-unity-build-specialist.md +25 -0
- package/.claude/{agents → nccgs/agent-library}/nccgs-unity-content-specialist.md +0 -1
- package/.claude/nccgs/agent-library/nccgs-unity-implementer.md +25 -0
- package/.claude/{agents → nccgs/agent-library}/nccgs-unity-rendering-specialist.md +0 -1
- package/.claude/nccgs/agent-library/nccgs-unity-systems-specialist.md +25 -0
- package/.claude/nccgs/agent-library/nccgs-unity-ui-specialist.md +25 -0
- package/.claude/{agents → nccgs/agent-library}/nccgs-ux-designer.md +0 -1
- package/.claude/nccgs/agent-library/nccgs-verification-engineer.md +25 -0
- package/.claude/{agents → nccgs/agent-library}/nccgs-world-builder.md +0 -1
- package/.claude/{agents → nccgs/agent-library}/nccgs-writer.md +0 -1
- package/.claude/nccgs/constitution.md +13 -0
- package/.claude/nccgs/hooks/agent-audit.mjs +6 -0
- package/.claude/nccgs/hooks/closure-guard.mjs +34 -0
- package/.claude/nccgs/hooks/runtime-observation.mjs +11 -0
- package/.claude/nccgs/hooks/session-start.mjs +8 -1
- package/.claude/nccgs/pipeline.json +176 -0
- package/.claude/nccgs/protocols/agent-contract.md +5 -0
- package/.claude/nccgs/protocols/astra-claude.md +77 -0
- package/.claude/nccgs/protocols/evidence.md +6 -0
- package/.claude/nccgs/protocols/model-routing.md +27 -5
- package/.claude/nccgs/protocols/orchestration.md +5 -0
- package/.claude/nccgs/protocols/task-gates.md +66 -0
- package/.claude/nccgs/routing.generated.md +11 -0
- package/.claude/nccgs/settings.fragment.json +73 -9
- package/.claude/nccgs/studio.json +187 -96
- package/.claude/nccgs/tools/compile-policy.mjs +83 -0
- package/.claude/nccgs/tools/configure-models.mjs +75 -35
- package/.claude/nccgs/tools/gates.mjs +167 -0
- package/.claude/nccgs/tools/policy.mjs +62 -0
- package/.claude/nccgs/tools/runtime.mjs +40 -0
- package/.claude/nccgs/tools/task.mjs +77 -0
- package/.claude/nccgs/workflow-catalog.json +90 -13
- package/.claude/skills/accessibility-review/SKILL.md +6 -5
- package/.claude/skills/accessibility-review/references/astra.md +17 -0
- package/.claude/skills/accessibility-review/references/legacy.md +7 -0
- package/.claude/skills/architecture-decision/SKILL.md +6 -5
- package/.claude/skills/architecture-decision/references/astra.md +12 -0
- package/.claude/skills/architecture-decision/references/legacy.md +7 -0
- package/.claude/skills/asset-audit/SKILL.md +6 -5
- package/.claude/skills/asset-audit/references/astra.md +17 -0
- package/.claude/skills/asset-audit/references/legacy.md +7 -0
- package/.claude/skills/audit/SKILL.md +6 -7
- package/.claude/skills/audit/references/astra.md +17 -0
- package/.claude/skills/audit/references/legacy.md +9 -0
- package/.claude/skills/balance-review/SKILL.md +6 -5
- package/.claude/skills/balance-review/references/astra.md +17 -0
- package/.claude/skills/balance-review/references/legacy.md +7 -0
- package/.claude/skills/bug-triage/SKILL.md +6 -5
- package/.claude/skills/bug-triage/references/astra.md +20 -0
- package/.claude/skills/bug-triage/references/legacy.md +7 -0
- package/.claude/skills/closure/SKILL.md +5 -4
- package/.claude/skills/closure/references/astra.md +15 -0
- package/.claude/skills/closure/references/legacy.md +7 -0
- package/.claude/skills/compatibility-review/SKILL.md +6 -5
- package/.claude/skills/compatibility-review/references/astra.md +17 -0
- package/.claude/skills/compatibility-review/references/legacy.md +7 -0
- package/.claude/skills/context-pack/SKILL.md +5 -4
- package/.claude/skills/context-pack/references/astra.md +12 -0
- package/.claude/skills/context-pack/references/legacy.md +7 -0
- package/.claude/skills/dependency-review/SKILL.md +6 -5
- package/.claude/skills/dependency-review/references/astra.md +17 -0
- package/.claude/skills/dependency-review/references/legacy.md +7 -0
- package/.claude/skills/design/SKILL.md +6 -11
- package/.claude/skills/design/references/astra.md +12 -0
- package/.claude/skills/design/references/legacy.md +13 -0
- package/.claude/skills/design-review/SKILL.md +6 -5
- package/.claude/skills/design-review/references/astra.md +17 -0
- package/.claude/skills/design-review/references/legacy.md +7 -0
- package/.claude/skills/evidence-review/SKILL.md +6 -5
- package/.claude/skills/evidence-review/references/astra.md +17 -0
- package/.claude/skills/evidence-review/references/legacy.md +7 -0
- package/.claude/skills/hotfix/SKILL.md +6 -5
- package/.claude/skills/hotfix/references/astra.md +23 -0
- package/.claude/skills/hotfix/references/legacy.md +7 -0
- package/.claude/skills/incident-recovery/SKILL.md +6 -5
- package/.claude/skills/incident-recovery/references/astra.md +20 -0
- package/.claude/skills/incident-recovery/references/legacy.md +7 -0
- package/.claude/skills/localize-game/SKILL.md +6 -5
- package/.claude/skills/localize-game/references/astra.md +20 -0
- package/.claude/skills/localize-game/references/legacy.md +7 -0
- package/.claude/skills/migrate-project/SKILL.md +6 -16
- package/.claude/skills/migrate-project/references/astra.md +23 -0
- package/.claude/skills/migrate-project/references/legacy.md +19 -0
- package/.claude/skills/migrate-project/references/procedure.md +8 -0
- package/.claude/skills/milestone-review/SKILL.md +6 -5
- package/.claude/skills/milestone-review/references/astra.md +12 -0
- package/.claude/skills/milestone-review/references/legacy.md +7 -0
- package/.claude/skills/nccgs-code-review/SKILL.md +6 -4
- package/.claude/skills/nccgs-code-review/references/astra.md +15 -0
- package/.claude/skills/nccgs-code-review/references/legacy.md +7 -0
- package/.claude/skills/performance-audit/SKILL.md +6 -5
- package/.claude/skills/performance-audit/references/astra.md +17 -0
- package/.claude/skills/performance-audit/references/legacy.md +7 -0
- package/.claude/skills/plan-feature/SKILL.md +6 -5
- package/.claude/skills/plan-feature/references/astra.md +12 -0
- package/.claude/skills/plan-feature/references/legacy.md +7 -0
- package/.claude/skills/playtest/SKILL.md +5 -4
- package/.claude/skills/playtest/references/astra.md +20 -0
- package/.claude/skills/playtest/references/legacy.md +7 -0
- package/.claude/skills/project-stage/SKILL.md +5 -4
- package/.claude/skills/project-stage/references/astra.md +12 -0
- package/.claude/skills/project-stage/references/legacy.md +7 -0
- package/.claude/skills/prototype-feature/SKILL.md +6 -5
- package/.claude/skills/prototype-feature/references/astra.md +20 -0
- package/.claude/skills/prototype-feature/references/legacy.md +7 -0
- package/.claude/skills/qa-plan/SKILL.md +6 -5
- package/.claude/skills/qa-plan/references/astra.md +12 -0
- package/.claude/skills/qa-plan/references/legacy.md +7 -0
- package/.claude/skills/release/SKILL.md +6 -9
- package/.claude/skills/release/references/astra.md +23 -0
- package/.claude/skills/release/references/legacy.md +11 -0
- package/.claude/skills/release-readiness/SKILL.md +6 -5
- package/.claude/skills/release-readiness/references/astra.md +17 -0
- package/.claude/skills/release-readiness/references/legacy.md +7 -0
- package/.claude/skills/retrospective/SKILL.md +6 -5
- package/.claude/skills/retrospective/references/astra.md +12 -0
- package/.claude/skills/retrospective/references/legacy.md +7 -0
- package/.claude/skills/review/SKILL.md +6 -7
- package/.claude/skills/review/references/astra.md +15 -0
- package/.claude/skills/review/references/legacy.md +9 -0
- package/.claude/skills/security-audit/SKILL.md +6 -5
- package/.claude/skills/security-audit/references/astra.md +17 -0
- package/.claude/skills/security-audit/references/legacy.md +7 -0
- package/.claude/skills/sprint-plan/SKILL.md +6 -5
- package/.claude/skills/sprint-plan/references/astra.md +12 -0
- package/.claude/skills/sprint-plan/references/legacy.md +7 -0
- package/.claude/skills/status/SKILL.md +5 -6
- package/.claude/skills/status/references/astra.md +13 -0
- package/.claude/skills/status/references/legacy.md +9 -0
- package/.claude/skills/story-readiness/SKILL.md +5 -4
- package/.claude/skills/story-readiness/references/astra.md +17 -0
- package/.claude/skills/story-readiness/references/legacy.md +7 -0
- package/.claude/skills/test/SKILL.md +6 -7
- package/.claude/skills/test/references/astra.md +20 -0
- package/.claude/skills/test/references/legacy.md +9 -0
- package/.claude/skills/ui-review/SKILL.md +6 -5
- package/.claude/skills/ui-review/references/astra.md +17 -0
- package/.claude/skills/ui-review/references/legacy.md +7 -0
- package/.claude/skills/work/SKILL.md +6 -27
- package/.claude/skills/work/references/astra.md +20 -0
- package/.claude/skills/work/references/closure.md +10 -0
- package/.claude/skills/work/references/feature-contracts.md +4 -0
- package/.claude/skills/work/references/legacy.md +29 -0
- package/README.md +59 -25
- package/UPGRADING.md +37 -0
- package/VERSION +1 -1
- package/docs/ARCHITECTURE.md +30 -13
- package/docs/HUONG-DAN-MIGRATE-VA-SU-DUNG.md +34 -329
- package/docs/MIGRATION-MATRIX.md +1 -1
- package/docs/NCCGS-1.3.md +125 -0
- package/docs/PROJECT-POLICY.md +18 -3
- package/docs/WORKFLOWS.md +19 -13
- package/package.json +5 -3
- package/scaffold/.nccgs/gates.json +7 -0
- package/scaffold/.nccgs/project.yaml +6 -3
- package/scaffold/.nccgs/state.md +1 -1
- package/scaffold/.nccgs/templates/agent-handoff.md +10 -0
- package/scaffold/.nccgs/templates/closure-record.md +11 -0
- package/scaffold/.nccgs/templates/feature-contract.md +9 -0
- package/scaffold/.nccgs/templates/implementation-report.md +49 -0
- package/scripts/cli.mjs +9 -4
- package/scripts/doctor.mjs +27 -1
- package/scripts/install.mjs +4 -3
- package/scripts/validate.mjs +20 -7
- package/tests/framework.test.mjs +143 -2
- package/tests/gates.test.mjs +170 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { spawnSync } from "node:child_process";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import test from "node:test";
|
|
8
|
+
import { initialize, snapshot, record, check, close, taskPath } from "../.claude/nccgs/tools/gates.mjs";
|
|
9
|
+
import { artifact, atomicJson, json, hash } from "../.claude/nccgs/tools/policy.mjs";
|
|
10
|
+
import { run } from "../.claude/nccgs/tools/task.mjs";
|
|
11
|
+
import { observe } from "../.claude/nccgs/tools/runtime.mjs";
|
|
12
|
+
|
|
13
|
+
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
14
|
+
function fixture(options = {}) {
|
|
15
|
+
const project = fs.mkdtempSync(path.join(os.tmpdir(), "nccgs-gates-"));
|
|
16
|
+
const write = (name, value) => {
|
|
17
|
+
const file = path.join(project, name);
|
|
18
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
19
|
+
fs.writeFileSync(file, typeof value === "string" ? value : JSON.stringify(value));
|
|
20
|
+
return artifact(project, name);
|
|
21
|
+
};
|
|
22
|
+
for (const file of ["pipeline.json", "studio.json"]) write(`.claude/nccgs/${file}`, fs.readFileSync(path.join(root, ".claude/nccgs", file), "utf8"));
|
|
23
|
+
const policy = write(".nccgs/project.yaml", "schema_version: 2\nmodels:\n profile: astra-claude\n");
|
|
24
|
+
write(".nccgs/gates.json", { schemaVersion: 1, policySha256: policy.sha256, requiredChecks: ["regression"], requiredArtifacts: options.artifacts ?? [], manualGates: options.manual ?? [] });
|
|
25
|
+
write("Assets/game.cs", "// initial implementation\n");
|
|
26
|
+
write(".nccgs/plan.md", "Astra plan: preserve health invariant and verify regression.");
|
|
27
|
+
initialize(project, "feature", ".nccgs/plan.md");
|
|
28
|
+
let index = 0;
|
|
29
|
+
const snap = () => snapshot(project).sha256;
|
|
30
|
+
const task = () => json(taskPath(project, "feature"));
|
|
31
|
+
function runtime(stage, overrides = {}) {
|
|
32
|
+
const source = `.nccgs/evidence/raw-${++index}.json`;
|
|
33
|
+
write(source, { taskId: "feature", snapshot: snap(), sessionId: "session", agentId: stage,
|
|
34
|
+
observedModel: stage === "implementation" ? "claude-opus-5-5" : "claude-fable-5-1",
|
|
35
|
+
observedEffort: stage === "implementation" ? "xhigh" : "high", capturedAt: new Date().toISOString(),
|
|
36
|
+
status: "completed", provenance: { kind: "provider-log", reference: "test fixture: captured runtime request metadata" }, ...overrides });
|
|
37
|
+
return run(["observe", "--project", project, "--id", "feature", "--file", source]);
|
|
38
|
+
}
|
|
39
|
+
const impl = (overrides = {}) => record(project, "feature", "implementation", { snapshot: snap(), runtime: runtime("implementation"),
|
|
40
|
+
checks: [{ name: "regression", status: "PASS", evidence: write(`.nccgs/evidence/test-${++index}.log`, "fixture regression: PASS") }], ...overrides });
|
|
41
|
+
const review = (overrides = {}) => record(project, "feature", "review", { snapshot: snap(), runtime: runtime("review"), verdict: "PASS", criteriaMet: true, findings: [], evidence: write(`.nccgs/evidence/review-${++index}.md`, "Fixture independent review findings: none"), ...overrides });
|
|
42
|
+
const report = (overrides = {}) => record(project, "feature", "report", { snapshot: snap(), evidence: write(`.nccgs/evidence/report-${++index}.md`, "Fixture report, review reference and evidence"), ...overrides });
|
|
43
|
+
const approval = (stage, overrides = {}) => record(project, "feature", stage, { snapshot: snap(), evidence: write(`.nccgs/approvals/${stage}-${++index}.json`, {
|
|
44
|
+
actor: stage === "astra" ? "Astra" : "Product Owner", decision: stage === "astra" ? "PASS" : "ACCEPTED",
|
|
45
|
+
taskId: "feature", snapshot: snap(), decidedAt: new Date().toISOString(), statement: "Explicit TEST FIXTURE approval", reference: "test-only external decision event", ...overrides }) });
|
|
46
|
+
return { project, write, snap, task, runtime, impl, review, report, approval,
|
|
47
|
+
dispose() {
|
|
48
|
+
const resolved = path.resolve(project);
|
|
49
|
+
assert.ok(resolved.startsWith(path.resolve(os.tmpdir()) + path.sep + "nccgs-gates-"));
|
|
50
|
+
fs.rmSync(resolved, { recursive: true, force: true });
|
|
51
|
+
} };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
test("end-to-end gates require Fable, Astra and explicit acceptance in order", () => {
|
|
55
|
+
const f = fixture();
|
|
56
|
+
try {
|
|
57
|
+
assert.throws(() => close(f.project, "feature"), /implementation: missing/);
|
|
58
|
+
assert.throws(() => f.report(), /implementation: missing/);
|
|
59
|
+
f.impl();
|
|
60
|
+
assert.throws(() => f.report(), /review: missing/);
|
|
61
|
+
f.review();
|
|
62
|
+
assert.equal(check(f.project, f.task(), "report").allowed, true);
|
|
63
|
+
f.report();
|
|
64
|
+
assert.throws(() => close(f.project, "feature"), /astra: missing/);
|
|
65
|
+
f.approval("astra");
|
|
66
|
+
assert.throws(() => close(f.project, "feature"), /acceptance: missing/);
|
|
67
|
+
f.approval("acceptance");
|
|
68
|
+
assert.equal(close(f.project, "feature").phase, "DONE");
|
|
69
|
+
} finally { f.dispose(); }
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("source additions, deletions and edits invalidate review and approvals", () => {
|
|
73
|
+
const f = fixture();
|
|
74
|
+
try {
|
|
75
|
+
f.impl(); f.review(); f.report(); f.approval("astra"); f.approval("acceptance"); close(f.project, "feature");
|
|
76
|
+
const before = f.snap();
|
|
77
|
+
f.write("Assets/new.cs", "untracked new source");
|
|
78
|
+
assert.notEqual(f.snap(), before);
|
|
79
|
+
assert.throws(() => close(f.project, "feature"), /stale/);
|
|
80
|
+
fs.unlinkSync(path.join(f.project, "Assets/game.cs"));
|
|
81
|
+
assert.throws(() => check(f.project, f.task(), "report"), /stale/);
|
|
82
|
+
f.impl();
|
|
83
|
+
assert.deepEqual(Object.keys(f.task().stages), ["implementation"]);
|
|
84
|
+
assert.equal(f.task().closed, undefined);
|
|
85
|
+
assert.ok(f.task().history.length > 0);
|
|
86
|
+
assert.throws(() => f.report(), /review: missing/);
|
|
87
|
+
} finally { f.dispose(); }
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("unknown effort, wrong model, fallback, and same-agent review cannot pass", () => {
|
|
91
|
+
const f = fixture();
|
|
92
|
+
try {
|
|
93
|
+
assert.throws(() => f.impl({ runtime: f.runtime("implementation", { observedEffort: "high" }) }), /effort/);
|
|
94
|
+
assert.throws(() => f.impl({ runtime: f.runtime("implementation", { observedModel: "claude-sonnet-4-6" }) }), /model/);
|
|
95
|
+
f.impl();
|
|
96
|
+
assert.throws(() => f.review({ runtime: f.runtime("review", { agentId: "implementation" }) }), /independent/);
|
|
97
|
+
assert.throws(() => f.review({ runtime: f.runtime("review", { modelsUsed: ["claude-fable-5-1", "claude-opus-5-5"] }) }), /fallback/);
|
|
98
|
+
const event = observe(f.project, { session_id: "session", tool_input: { subagent_type: "nccgs-adversarial-reviewer" }, tool_response: { resolvedModel: "claude-fable-5-1", status: "completed", agentId: "native-review" } }, "agent_result");
|
|
99
|
+
assert.equal(event.observedEffort, null);
|
|
100
|
+
assert.equal(event.requestedEffort, "high");
|
|
101
|
+
assert.ok(event.missing.includes("effort"));
|
|
102
|
+
const ref = f.write(".nccgs/evidence/unknown-effort.json", { ...event, taskId: "feature", snapshot: f.snap() });
|
|
103
|
+
assert.throws(() => f.review({ runtime: ref }), /effort/);
|
|
104
|
+
} finally { f.dispose(); }
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("negative review and rejection remain blocking; amended evidence invalidates results", () => {
|
|
108
|
+
const f = fixture();
|
|
109
|
+
try {
|
|
110
|
+
f.impl(); f.review({ verdict: "CHANGES_REQUIRED", findings: [{ severity: "HIGH", status: "OPEN" }] });
|
|
111
|
+
assert.throws(() => f.report(), /not passed/);
|
|
112
|
+
assert.throws(() => f.review({ findings: [{ severity: "HIGH", status: "OPEN" }] }), /blocking findings/);
|
|
113
|
+
f.review(); f.report(); f.approval("astra"); f.approval("acceptance");
|
|
114
|
+
f.approval("acceptance", { decision: "REJECTED" });
|
|
115
|
+
assert.throws(() => close(f.project, "feature"), /explicit Product Owner/);
|
|
116
|
+
const evidence = f.task().stages.review.evidence;
|
|
117
|
+
f.write(evidence.path, "altered review");
|
|
118
|
+
assert.throws(() => check(f.project, f.task(), "report"), /stale artifact/);
|
|
119
|
+
} finally { f.dispose(); }
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test("required checks, closure artifacts, manual gates and policy hashes are enforced", () => {
|
|
123
|
+
const f = fixture({ artifacts: [".nccgs/evidence/build.log"], manual: ["playtest"] });
|
|
124
|
+
try {
|
|
125
|
+
assert.throws(() => f.impl({ checks: [{ name: "unrelated", status: "PASS", evidence: f.write(".nccgs/evidence/unrelated.log", "PASS") }] }), /Required check missing/);
|
|
126
|
+
f.impl(); f.review();
|
|
127
|
+
assert.throws(() => f.report(), /closure artifact missing/);
|
|
128
|
+
f.report({ artifacts: [f.write(".nccgs/evidence/build.log", "Build successful")] });
|
|
129
|
+
f.approval("astra");
|
|
130
|
+
assert.throws(() => f.approval("acceptance"), /Manual gate missing/);
|
|
131
|
+
f.approval("acceptance", { gates: [{ id: "playtest", decision: "ACCEPTED", reference: "test fixture playtest decision" }] });
|
|
132
|
+
close(f.project, "feature");
|
|
133
|
+
f.write(".nccgs/plan.md", "Changed plan");
|
|
134
|
+
assert.throws(() => close(f.project, "feature"), /stale artifact/);
|
|
135
|
+
} finally { f.dispose(); }
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test("path traversal and premature/fabricated external sign-offs fail", () => {
|
|
139
|
+
const f = fixture();
|
|
140
|
+
try {
|
|
141
|
+
assert.throws(() => taskPath(f.project, "../escape"), /Invalid task/);
|
|
142
|
+
assert.throws(() => artifact(f.project, "../outside.txt"), /escapes/);
|
|
143
|
+
f.impl(); f.review(); f.report();
|
|
144
|
+
assert.throws(() => f.approval("astra", { actor: "Claude" }), /explicit Astra/);
|
|
145
|
+
assert.throws(() => f.approval("astra", { decidedAt: "2000-01-01T00:00:00.000Z" }), /predates/);
|
|
146
|
+
const old = process.env.CLAUDECODE;
|
|
147
|
+
try { process.env.CLAUDECODE = "1"; assert.throws(() => f.approval("astra"), /outside the Claude/); }
|
|
148
|
+
finally { if (old === undefined) delete process.env.CLAUDECODE; else process.env.CLAUDECODE = old; }
|
|
149
|
+
const guard = spawnSync(process.execPath, [path.join(root, ".claude/nccgs/hooks/closure-guard.mjs"), "pre"], {
|
|
150
|
+
cwd: f.project, encoding: "utf8", input: JSON.stringify({ session_id: "session", tool_input: { file_path: path.join(f.project, ".nccgs/state.md"), content: "Final state: DONE" } }) });
|
|
151
|
+
assert.equal(guard.status, 2);
|
|
152
|
+
assert.match(guard.stderr, /bound to an NCCGS task/);
|
|
153
|
+
} finally { f.dispose(); }
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test("verification runner captures real exit status and rejects changed logs", () => {
|
|
157
|
+
const f = fixture();
|
|
158
|
+
try {
|
|
159
|
+
const execute = source => run(["verify", "--project", f.project, "--id", "feature", "--name", "regression", "--", process.execPath, "-e", source]);
|
|
160
|
+
const failed = execute("process.exit(3)");
|
|
161
|
+
assert.equal(failed.status, "FAIL");
|
|
162
|
+
assert.throws(() => f.impl({ checks: [failed] }), /failed\/unproven/);
|
|
163
|
+
const passed = execute("console.log('fixture verification passed')");
|
|
164
|
+
assert.equal(passed.status, "PASS");
|
|
165
|
+
f.impl({ checks: [passed] });
|
|
166
|
+
const execution = json(path.join(f.project, passed.evidence.path));
|
|
167
|
+
f.write(execution.log.path, "tampered log");
|
|
168
|
+
assert.throws(() => check(f.project, f.task(), "review"), /stale artifact/);
|
|
169
|
+
} finally { f.dispose(); }
|
|
170
|
+
});
|