@mechanai/deepreview 2.5.0 → 2.6.1

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.
@@ -19,7 +19,16 @@ You will receive a path to an input file. This may be a diff, a spec, a plan, or
19
19
 
20
20
  ## Prior Context (if provided)
21
21
 
22
- Your prompt may include sections titled "Design Decisions", "Prior Findings", and "Covered Regions". Rules: do NOT flag design decisions as issues; do NOT re-report prior findings; prioritize uncovered regions but you may still report _new_ issues in covered regions.
22
+ Your prompt may include sections titled "Project Context", "Design Decisions", "Prior Findings", and "Covered Regions". Rules:
23
+
24
+ - **Project Context:** If your prompt includes a "Project Context" section (version, deployment model, status), use it to calibrate severity:
25
+ - v0.x.0 projects: breaking changes are expected and acceptable per semver — flag them as **suggestion**, not **critical** or **warning**. The change "API breaking changes are expected" still applies at v0.x.0.
26
+ - v1+ public libraries: breaking changes require major version bump — flag as **critical** or **warning**.
27
+ - Internal tools with no external consumers: breaking changes are **suggestion**-level (internal reorganization risk only).
28
+ - Private/unpublished packages: breaking changes are **suggestion**, unless the project explicitly targets v1+ stability.
29
+ - **Design Decisions:** Do NOT flag design decisions as issues; do NOT suggest alternatives.
30
+ - **Prior Findings:** Do NOT re-report prior findings.
31
+ - **Covered Regions:** Prioritize uncovered regions but you may still report _new_ issues in covered regions.
23
32
 
24
33
  Your prompt may also begin with framing directives (e.g., novelty-seeking instructions). Follow those directives in addition to the rules above.
25
34
 
@@ -57,9 +66,11 @@ Write your review to the output path provided. Use this format for each finding:
57
66
 
58
67
  Severity guide:
59
68
 
60
- - **critical:** Public API or data contract broken with no migration path
61
- - **warning:** Behavior change that may break some consumers silently
62
- - **suggestion:** Internal change that could become breaking if exposed later
69
+ - **critical:** Public API or data contract broken with no migration path (or v1+ library with breaking change and no major version bump)
70
+ - **warning:** Behavior change that may break some consumers silently (or v1+ breaking change that should have bumped major version)
71
+ - **suggestion:** Internal change that could become breaking if exposed later (or v0.x.0 breaking API change — these are expected per semver)
72
+
73
+ Note: For v0.x.0 projects, apply the semver exemption — breaking changes are expected during pre-1.0 development. Downgrade breaking API changes to **suggestion** unless they create data loss or irreversible corruption.
63
74
 
64
75
  If you find no issues, write: "No compatibility issues found."
65
76
 
@@ -19,7 +19,15 @@ You will receive a path to an input file. This may be a diff, a spec, a plan, or
19
19
 
20
20
  ## Prior Context (if provided)
21
21
 
22
- Your prompt may include sections titled "Design Decisions", "Prior Findings", and "Covered Regions". Rules: do NOT flag design decisions as issues; do NOT re-report prior findings; prioritize uncovered regions but you may still report _new_ issues in covered regions.
22
+ Your prompt may include sections titled "Project Context", "Design Decisions", "Prior Findings", and "Covered Regions". Rules:
23
+
24
+ - **Project Context:** If your prompt includes a "Project Context" section (version, deployment model), use it to calibrate severity:
25
+ - v0.x.0 projects are in active development: stale comments and docs are **suggestion**-level, not **critical** or **warning**.
26
+ - Published libraries (v1+) must maintain accurate docs: flag stale docs that contradict code as **critical** or **warning**.
27
+ - Internal tools have lower documentation burden than public APIs: cosmetic doc issues are **suggestion**, not **warning**.
28
+ - **Design Decisions:** Do NOT flag design decisions as issues; do NOT suggest alternatives.
29
+ - **Prior Findings:** Do NOT re-report prior findings.
30
+ - **Covered Regions:** Prioritize uncovered regions but you may still report _new_ issues in covered regions.
23
31
 
24
32
  Your prompt may also begin with framing directives (e.g., novelty-seeking instructions). Follow those directives in addition to the rules above.
25
33
 
@@ -5,12 +5,12 @@ temperature: 0.1
5
5
  permission:
6
6
  # Read access is implicitly unrestricted (OpenCode default) — needed to inspect source files.
7
7
  edit:
8
- ".ai/deepreview/**": allow
9
8
  "*": deny
9
+ ".ai/deepreview/**": allow
10
10
  bash:
11
+ "*": deny
11
12
  "git log*": allow
12
13
  "git blame*": allow
13
- "*": deny
14
14
  ---
15
15
 
16
16
  You are a skeptical senior engineer. Your job is to independently verify each proposed fix in an implementation plan before it gets applied to the codebase. You are not here to agree with the planner — you are here to catch mistakes.
@@ -19,7 +19,16 @@ You will receive a path to an input file. This may be a diff, a spec, a plan, or
19
19
 
20
20
  ## Prior Context (if provided)
21
21
 
22
- Your prompt may include sections titled "Design Decisions", "Prior Findings", and "Covered Regions". Rules: do NOT flag design decisions as issues; do NOT re-report prior findings; prioritize uncovered regions but you may still report _new_ issues in covered regions.
22
+ Your prompt may include sections titled "Project Context", "Design Decisions", "Prior Findings", and "Covered Regions". Rules:
23
+
24
+ - **Project Context:** If your prompt includes a "Project Context" section (version, deployment model, threat model), use it to calibrate severity:
25
+ - Localhost-only tools have no network threat model: downgrade auth/network findings and missing security headers to **suggestion**-level, not **warning**.
26
+ - v0.x.0 projects may skip some production security checks: downgrade findings about production hardening to **suggestion**, not **critical** or **warning**.
27
+ - Internal-network tools are lower-threat than public-facing services: downgrade findings about external attack vectors to **suggestion**.
28
+ - Published libraries (v1+) must follow production security standards: flag unvalidated input and auth gaps as **critical** or **warning**.
29
+ - **Design Decisions:** Do NOT flag design decisions as issues; do NOT suggest alternatives.
30
+ - **Prior Findings:** Do NOT re-report prior findings.
31
+ - **Covered Regions:** Prioritize uncovered regions but you may still report _new_ issues in covered regions.
23
32
 
24
33
  Your prompt may also begin with framing directives (e.g., novelty-seeking instructions). Follow those directives in addition to the rules above.
25
34
 
@@ -21,8 +21,20 @@ Set ALL_SESSION_DIRS=[] (list of all session directories used, in order)
21
21
  Determine REPO_ROOT — the main repository root (not a worktree root). Run:
22
22
  `REPO_ROOT=$(realpath "$(git rev-parse --git-common-dir)" | sed 's|/\.git$||')`
23
23
 
24
- If CONTEXT_FILE exists, read its contents and set PRIOR_CONTEXT to:
25
- "## Design Decisions (intentional — do not flag)\nThe following are deliberate design choices. Do NOT flag these as issues or suggest alternatives.\n`\n" + contents of CONTEXT_FILE + "\n`\n"
24
+ Extract PROJECT_CONTEXT by detecting project metadata (version, deployment model, publish status):
25
+
26
+ - Check for package.json or Cargo.toml to detect version and publish status
27
+ - Check for .deepreview.yml to detect explicit deployment model (threatModel field)
28
+ - If no .deepreview.yml exists, infer deployment model: v0.x.0 and private packages are "internal-network", v1+.x.x and public are "public-facing", otherwise "unknown"
29
+ - Format as a calibration preamble with version info, deployment model, and guidance for severity adjustment
30
+ - If metadata extraction fails or no version info is found, set PROJECT_CONTEXT="" (empty string)
31
+
32
+ Build PRIOR_CONTEXT:
33
+
34
+ - Start with PROJECT_CONTEXT (if non-empty)
35
+ - If CONTEXT_FILE exists, append:
36
+ "## Design Decisions (intentional — do not flag)\nThe following are deliberate design choices. Do NOT flag these as issues or suggest alternatives.\n`\n" + contents of CONTEXT_FILE + "\n`\n"
37
+ - If neither PROJECT_CONTEXT nor CONTEXT_FILE exist, set PRIOR_CONTEXT="" (empty string)
26
38
 
27
39
  STEP 2: RUN INITIAL DEEPREVIEW (full pipeline with cross-validation)
28
40
  Run the full deepreview pipeline (Stages 1-5 from the deepreview command):
@@ -15,7 +15,21 @@ STEP 1: DETERMINE INPUT
15
15
  - Set ALL_SESSION_DIRS=[] (list of all session directories used, in order)
16
16
  - Determine REPO_ROOT — the main repository root (not a worktree root). Run:
17
17
  `REPO_ROOT=$(realpath "$(git rev-parse --git-common-dir)" | sed 's|/\.git$||')`
18
- - If CONTEXT_FILE exists, set PRIOR_CONTEXT="## Design Decisions (intentional — do not flag)\nThe following are deliberate design choices. Do NOT flag these as issues or suggest alternatives.\n`\n" + contents of CONTEXT_FILE + "\n`\n\n"
18
+
19
+ Extract PROJECT_CONTEXT by detecting project metadata (version, deployment model, publish status):
20
+
21
+ - Check for package.json or Cargo.toml to detect version and publish status
22
+ - Check for .deepreview.yml to detect explicit deployment model (threatModel field)
23
+ - If no .deepreview.yml exists, infer deployment model: v0.x.0 and private packages are "internal-network", v1+.x.x and public are "public-facing", otherwise "unknown"
24
+ - Format as a calibration preamble with version info, deployment model, and guidance for severity adjustment
25
+ - If metadata extraction fails or no version info is found, set PROJECT_CONTEXT="" (empty string)
26
+
27
+ Build PRIOR_CONTEXT:
28
+
29
+ - Start with PROJECT_CONTEXT (if non-empty)
30
+ - If CONTEXT_FILE exists, append:
31
+ "## Design Decisions (intentional — do not flag)\nThe following are deliberate design choices. Do NOT flag these as issues or suggest alternatives.\n`\n" + contents of CONTEXT_FILE + "\n`\n\n"
32
+ - If neither PROJECT_CONTEXT nor CONTEXT_FILE exist, set PRIOR_CONTEXT="" (empty string)
19
33
 
20
34
  STEP 2: RUN INITIAL DEEPREVIEW-SPEC (full pipeline with cross-validation)
21
35
  Run the full deepreview-spec pipeline (Stages 1-5 from the deepreview-spec command):
@@ -23,10 +23,21 @@ STEP 2: PREPARE INPUT
23
23
 
24
24
  Set INPUT_DESCRIPTION="the following spec/plan files: $ARGUMENTS"
25
25
 
26
- If CONTEXT_FILE exists, set DESIGN_CONTEXT to the contents of that file. Build a CONTEXT_PREAMBLE:
26
+ Extract PROJECT_CONTEXT by detecting project metadata (version, deployment model, publish status):
27
+
28
+ - Check for package.json or Cargo.toml to detect version and publish status
29
+ - Check for .deepreview.yml to detect explicit deployment model (threatModel field)
30
+ - If no .deepreview.yml exists, infer deployment model: v0.x.0 and private packages are "internal-network", v1+.x.x and public are "public-facing", otherwise "unknown"
31
+ - Format as a calibration preamble with version info, deployment model, and guidance for severity adjustment
32
+ - If metadata extraction fails or no version info is found, set PROJECT_CONTEXT="" (empty string)
33
+
34
+ Build CONTEXT_PREAMBLE:
35
+ If PROJECT_CONTEXT is not empty, start with: "${PROJECT_CONTEXT}\n"
36
+
37
+ If CONTEXT_FILE exists, append:
27
38
  "## Design Decisions (intentional — do not flag)\nThe following are deliberate design choices. Do NOT flag these as issues or suggest alternatives.\n`\n$DESIGN_CONTEXT\n`\n\n"
28
39
 
29
- If CONTEXT_FILE does not exist, set CONTEXT_PREAMBLE="" (empty string).
40
+ If both PROJECT_CONTEXT and CONTEXT_FILE are empty, set CONTEXT_PREAMBLE="" (empty string).
30
41
 
31
42
  STEP 3: DISPATCH STAGE 1 — INITIAL REVIEW (5 parallel tasks)
32
43
  Dispatch ALL FIVE of these Task tool calls simultaneously in a single message:
@@ -41,10 +41,37 @@ Set INPUT_DESCRIPTION based on mode:
41
41
  - MODE=branch: "a branch diff against main"
42
42
  - MODE=files: "the following files: <list of filenames>"
43
43
 
44
+ STEP 2a: EXTRACT PROJECT CONTEXT
45
+ Build PROJECT_CONTEXT by extracting metadata (version, deployment model, publish status) from the repo:
46
+
47
+ - Check for package.json or Cargo.toml to detect version and publish status
48
+ - Check for .deepreview.yml to detect explicit deployment model (threat-model field)
49
+ - If no .deepreview.yml exists, infer deployment model: v0.x.0 and private packages are "internal-network", v1+.x.x and public are "public-facing", otherwise "unknown"
50
+ - Format as:
51
+
52
+ ```
53
+ ## Project Context (for severity calibration)
54
+
55
+ **Name:** [project name]
56
+ **Version:** [version] (if v0.x.0, include note: "pre-1.0 — relaxed API stability expectations")
57
+ **Deployment:** [localhost-only|internal-network|public-facing|library] [threat model note]
58
+ **Status:** [Private/internal|Published]
59
+
60
+ Use this context to calibrate finding severity. For example:
61
+ - v0.1.0 projects may have API instability — flag as **suggestion**, not **warning**.
62
+ - Localhost-only tools have no network threat model — downgrade auth/network findings to **suggestion**.
63
+ - Stale docs in pre-1.0 projects are **suggestion**-level, not critical.
64
+ ```
65
+
66
+ If metadata extraction fails or no version info is found, set PROJECT_CONTEXT="" (empty string).
67
+
68
+ STEP 2b: BUILD CONTEXT PREAMBLE
44
69
  If CONTEXT_FILE exists, set DESIGN_CONTEXT to the contents of that file. Build a CONTEXT_PREAMBLE:
45
- "## Design Decisions (intentional — do not flag)\nThe following are deliberate design choices. Do NOT flag these as issues or suggest alternatives.\n`\n$DESIGN_CONTEXT\n`\n\n"
70
+ "${PROJECT_CONTEXT}## Design Decisions (intentional — do not flag)\nThe following are deliberate design choices. Do NOT flag these as issues or suggest alternatives.\n`\n$DESIGN_CONTEXT\n`\n\n"
71
+
72
+ If CONTEXT_FILE does not exist and PROJECT_CONTEXT is not empty, set CONTEXT_PREAMBLE to just "${PROJECT_CONTEXT}\n"
46
73
 
47
- If CONTEXT_FILE does not exist, set CONTEXT_PREAMBLE="" (empty string).
74
+ If both are empty, set CONTEXT_PREAMBLE="" (empty string).
48
75
 
49
76
  STEP 3: DISPATCH STAGE 1 — INITIAL REVIEW (5 parallel tasks)
50
77
  Dispatch ALL FIVE of these Task tool calls simultaneously in a single message:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mechanai/deepreview",
3
- "version": "2.5.0",
3
+ "version": "2.6.1",
4
4
  "description": "Multi-agent parallel code/spec review for OpenCode",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -0,0 +1,80 @@
1
+ import { describe, it, expect } from "bun:test";
2
+ import { getProjectMetadata, formatProjectContextPreamble } from "./project-context";
3
+ import path from "node:path";
4
+
5
+ describe("project-context: metadata extraction", () => {
6
+ it("should extract metadata from package.json", () => {
7
+ const repoRoot = path.resolve(import.meta.dirname, "..");
8
+ const metadata = getProjectMetadata(repoRoot);
9
+
10
+ expect(metadata).toBeDefined();
11
+ expect(metadata.version).toBe("0.0.0-development");
12
+ expect(metadata.name).toBe("@mechanai/deepreview");
13
+ expect(metadata.deploymentModel).toBeDefined();
14
+ });
15
+ });
16
+
17
+ describe("project-context: preamble version handling", () => {
18
+ it("should format project context preamble for v0 projects", () => {
19
+ const metadata = {
20
+ version: "0.1.0",
21
+ name: "test-project",
22
+ isPrivate: true,
23
+ deploymentModel: "internal-network" as const,
24
+ description: "A test project",
25
+ };
26
+
27
+ const preamble = formatProjectContextPreamble(metadata);
28
+
29
+ expect(preamble).toContain("Project Context");
30
+ expect(preamble).toContain("0.1.0");
31
+ expect(preamble).toContain("pre-1.0");
32
+ expect(preamble).toContain("internal-network");
33
+ expect(preamble).toContain("severity calibration");
34
+ });
35
+
36
+ it("should handle v1+ versions without pre-1.0 note", () => {
37
+ const metadata = {
38
+ version: "1.2.3",
39
+ name: "stable-project",
40
+ isPrivate: false,
41
+ deploymentModel: "public-facing" as const,
42
+ };
43
+
44
+ const preamble = formatProjectContextPreamble(metadata);
45
+
46
+ expect(preamble).toContain("1.2.3");
47
+ expect(preamble).not.toMatch(/1\.2\.3.*pre-1\.0/u);
48
+ expect(preamble).toContain("public-facing");
49
+ });
50
+ });
51
+
52
+ describe("project-context: preamble deployment guidance", () => {
53
+ it("should include deployment guidance for localhost tools", () => {
54
+ const metadata = {
55
+ version: "0.1.0",
56
+ name: "dev-tool",
57
+ isPrivate: true,
58
+ deploymentModel: "localhost-only" as const,
59
+ };
60
+
61
+ const preamble = formatProjectContextPreamble(metadata);
62
+
63
+ expect(preamble).toContain("localhost-only");
64
+ expect(preamble).toContain("network/auth concerns");
65
+ });
66
+
67
+ it("should include guidance for published libraries", () => {
68
+ const metadata = {
69
+ version: "2.0.0",
70
+ name: "published-lib",
71
+ isPrivate: false,
72
+ deploymentModel: "library" as const,
73
+ };
74
+
75
+ const preamble = formatProjectContextPreamble(metadata);
76
+
77
+ expect(preamble).toContain("library");
78
+ expect(preamble).toContain("API stability");
79
+ });
80
+ });
@@ -0,0 +1,215 @@
1
+ /**
2
+ * Project context detection and formatting for severity calibration in reviewers.
3
+ *
4
+ * Extracts metadata from package.json, Cargo.toml, and .deepreview.yml to build
5
+ * a preamble that helps reviewers calibrate finding severity appropriately.
6
+ */
7
+
8
+ import { existsSync, readFileSync } from "node:fs";
9
+ import path from "node:path";
10
+ import { load as loadYaml } from "js-yaml";
11
+
12
+ export interface ProjectMetadata {
13
+ /** Semantic version (e.g., "0.1.0", "3.2.1") */
14
+ version?: string;
15
+ /** Whether the project is marked as private/internal */
16
+ isPrivate?: boolean;
17
+ /** Deployment model: "localhost-only" | "internal-network" | "public-facing" | "library" | "unknown" */
18
+ deploymentModel?: string;
19
+ /** True if project has publish = false in Cargo.toml */
20
+ isUnpublished?: boolean;
21
+ /** Description of the project */
22
+ description?: string;
23
+ /** Name of the project */
24
+ name?: string;
25
+ }
26
+
27
+ export interface DeepReviewConfig {
28
+ threatModel?: "localhost-only" | "internal-network" | "public-facing" | "library";
29
+ /** Additional context hints for reviewers */
30
+ context?: string;
31
+ }
32
+
33
+ interface PackageJson {
34
+ name?: string;
35
+ version?: string;
36
+ description?: string;
37
+ private?: boolean;
38
+ }
39
+
40
+ function parsePackageJson(filePath: string): Partial<ProjectMetadata> {
41
+ try {
42
+ const content = readFileSync(filePath, "utf-8");
43
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- Why: JSON.parse returns any; fields are all optional strings/booleans
44
+ const pkg = JSON.parse(content) as PackageJson;
45
+ return {
46
+ version: pkg.version,
47
+ isPrivate: pkg.private === true,
48
+ name: pkg.name,
49
+ description: pkg.description,
50
+ };
51
+ } catch {
52
+ return {};
53
+ }
54
+ }
55
+
56
+ function parseCargoToml(filePath: string): Partial<ProjectMetadata> {
57
+ try {
58
+ const content = readFileSync(filePath, "utf-8");
59
+ const versionMatch = /^version\s*=\s*["']([^"']+)["']/u.exec(content);
60
+ const nameMatch = /^name\s*=\s*["']([^"']+)["']/u.exec(content);
61
+ const publishMatch = /^\[publish\]/u.exec(content);
62
+ const descMatch = /^description\s*=\s*["']([^"']+)["']/u.exec(content);
63
+
64
+ return {
65
+ version: versionMatch?.[1],
66
+ name: nameMatch?.[1],
67
+ description: descMatch?.[1],
68
+ isUnpublished: publishMatch !== null,
69
+ };
70
+ } catch {
71
+ return {};
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Parse .deepreview.yml and extract deployment model.
77
+ */
78
+ function parseDeepReviewConfig(filePath: string): DeepReviewConfig {
79
+ try {
80
+ const content = readFileSync(filePath, "utf-8");
81
+ // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- Why: loadYaml returns unknown; fields are all optional with safe defaults
82
+ const result = loadYaml(content) as DeepReviewConfig | null;
83
+ return result ?? {};
84
+ } catch {
85
+ return {};
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Detect deployment model from project structure and metadata.
91
+ */
92
+ function detectDeploymentModel(metadata: ProjectMetadata, config: DeepReviewConfig): string {
93
+ // Explicit config wins
94
+ if (config.threatModel) {
95
+ return config.threatModel;
96
+ }
97
+
98
+ // Infer from metadata
99
+ if (metadata.isPrivate === false || metadata.isUnpublished === false) {
100
+ return "public-facing";
101
+ }
102
+ if (metadata.isPrivate === true) {
103
+ return "internal-network";
104
+ }
105
+
106
+ return "unknown";
107
+ }
108
+
109
+ /**
110
+ * Extract all project metadata from the repository root.
111
+ */
112
+ export function getProjectMetadata(
113
+ repoRoot: string,
114
+ ): ProjectMetadata & { deploymentModel: string } {
115
+ // Try package.json first (Node.js projects)
116
+ const packageJsonPath = path.join(repoRoot, "package.json");
117
+ let metadata: Partial<ProjectMetadata> = existsSync(packageJsonPath)
118
+ ? parsePackageJson(packageJsonPath)
119
+ : {};
120
+
121
+ // Try Cargo.toml (Rust projects) — takes precedence for version/name if both exist
122
+ const cargoTomlPath = path.join(repoRoot, "Cargo.toml");
123
+ if (existsSync(cargoTomlPath)) {
124
+ metadata = { ...metadata, ...parseCargoToml(cargoTomlPath) };
125
+ }
126
+
127
+ // Try .deepreview.yml for explicit configuration
128
+ const deepReviewConfigPath = path.join(repoRoot, ".deepreview.yml");
129
+ const config: DeepReviewConfig = existsSync(deepReviewConfigPath)
130
+ ? parseDeepReviewConfig(deepReviewConfigPath)
131
+ : {};
132
+
133
+ const deploymentModel = detectDeploymentModel(metadata as ProjectMetadata, config);
134
+
135
+ return {
136
+ ...(metadata as ProjectMetadata),
137
+ deploymentModel,
138
+ };
139
+ }
140
+
141
+ /**
142
+ * Get deployment context description for severity calibration.
143
+ */
144
+ function getDeploymentContext(deploymentModel: string): string {
145
+ switch (deploymentModel) {
146
+ case "localhost-only":
147
+ return "(localhost-only dev tools — network/auth concerns are lower priority)";
148
+ case "internal-network":
149
+ return "(internal tool — auth is important but threat model is limited to internal users)";
150
+ case "public-facing":
151
+ return "(public-facing service — threat model includes untrusted users)";
152
+ case "library":
153
+ return "(published library — API stability and backwards compatibility are critical)";
154
+ default:
155
+ return "";
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Build calibration guidelines for severity assessment.
161
+ */
162
+ function getCalibrationGuidelines(): string[] {
163
+ return [
164
+ "- Pre-1.0 projects: breaking API changes are expected — flag as suggestion, not warning",
165
+ "- Localhost-only tools: network/auth concerns are lower priority — downgrade to suggestion",
166
+ "- Published libraries (v1+): API stability and auth are critical — flag violations as warning/critical",
167
+ "- Stale docs in pre-1.0 projects: flag as suggestion, not critical",
168
+ ];
169
+ }
170
+
171
+ /**
172
+ * Format project metadata as a context preamble for reviewers.
173
+ * This is prepended to PRIOR_CONTEXT and helps reviewers calibrate severity.
174
+ */
175
+ export function formatProjectContextPreamble(
176
+ metadata: ProjectMetadata & { deploymentModel: string },
177
+ ): string {
178
+ const lines: string[] = ["## Project Context (for severity calibration)"];
179
+
180
+ if (metadata.name !== undefined && metadata.name !== "") {
181
+ lines.push(`**Name:** ${metadata.name}`);
182
+ }
183
+
184
+ if (metadata.version !== undefined && metadata.version !== "") {
185
+ const versionParts = metadata.version.split(".");
186
+ const majorVersion = Number.parseInt(versionParts[0] ?? "0", 10);
187
+ if (majorVersion === 0) {
188
+ lines.push(
189
+ `**Version:** ${metadata.version} (pre-1.0 — relaxed API stability expectations; breaking changes are expected)`,
190
+ );
191
+ } else {
192
+ lines.push(`**Version:** ${metadata.version}`);
193
+ }
194
+ }
195
+
196
+ if (metadata.deploymentModel !== "") {
197
+ const deploymentContext = getDeploymentContext(metadata.deploymentModel);
198
+ lines.push(`**Deployment:** ${metadata.deploymentModel} ${deploymentContext}`);
199
+ }
200
+
201
+ if (metadata.isPrivate === true) {
202
+ lines.push("**Status:** Private/internal project (no external consumers)");
203
+ } else if (metadata.isUnpublished === false) {
204
+ lines.push("**Status:** Published (breaking changes require major version bump)");
205
+ }
206
+
207
+ if (metadata.description !== undefined && metadata.description !== "") {
208
+ lines.push(`**Purpose:** ${metadata.description}`);
209
+ }
210
+
211
+ lines.push("\nUse this context to calibrate finding severity. Calibration guidelines:");
212
+ lines.push(...getCalibrationGuidelines());
213
+
214
+ return lines.join("\n") + "\n";
215
+ }