Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit
- #16 Python generated/private/dependency roots are excluded from discovery and status census: `.venv`, `venv`, `env`, `__pycache__`, `.eggs`, `build`, `.tox`, `.mypy_cache`, `.pytest_cache`, `.ruff_cache`, `site-packages`, `*.egg-info`, and `*.dist-info` - WHY: dependency/cache artifacts must not create graph freshness or coverage evidence.
- #17 Python export metadata is best-effort: `__all__` wins when present, otherwise the leading-underscore convention marks module-level public names; file `exports[]` entries must include policy and supportedSymbol - WHY: Python has no enforced export boundary.
- #244 `python.syntax` resolves one concrete interpreter through the Python project resolver, executes that exact interpreter with an isolated versioned JSON compile protocol over sorted `.py`/`.pyi` after-state content, and reports ranged/provenanced compiler diagnostics plus fine-grained outcomes. Parser-success overrides and hand-written grammar heuristics are forbidden; malformed output, nonzero exits, signals, and timeouts fail closed - WHY: syntax truth, target-version truth, and tool provenance must come from the same compiler invocation without mutating the source repo.
- #246 makes `@the-open-engine/opcore-validation-python` the sole dynamic owner of `opcore.python.project-context.v1`: every Python target resolves against its nearest project boundary through an injected read/list/exists/realpath workspace view, smol-toml AST config/build metadata, exact interpreter/tool/build probes, and after-state content. Missing realpath evidence is ambiguous, deleted overlays cannot remain discovery markers, and declared constraints never become invented exact versions. Validation, status, scan, init/install preview, metrics, ASP, and installed execution must reuse the resulting project key, context fingerprint, outcome, and provenance; ASP workspace/config reads must remain host-callback-only. Static descriptors advertise only the schema, outcome vocabulary, read-only behavior, and no-install guarantee - WHY: root-scoped and duplicate project/environment discovery validates nested monorepo files with the wrong interpreter and makes surfaces disagree.
- #19 requires graph status to preserve real WAL checkpoint evidence from the latest pipeline summary and release gates to fail missing/fabricated WAL evidence - WHY: freshness and checkpoint pressure must remain host-visible provider facts.
- #19 treats `opcore graph serve` as the stdio/MCP hot-query replacement, not a Unix socket, with parallel independent serve sessions as the supported concurrency evidence.
- #19 keeps current external CRG receipts as non-implementation compatibility evidence only - WHY: old CRG remains a guardrail until downstream cutover issues consume the Opcore proof.
Expand Down
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ Opcore is the code-intelligence and robustness monorepo for graph context, edit
- #16 Python generated/private/dependency roots are excluded from discovery and status census: `.venv`, `venv`, `env`, `__pycache__`, `.eggs`, `build`, `.tox`, `.mypy_cache`, `.pytest_cache`, `.ruff_cache`, `site-packages`, `*.egg-info`, and `*.dist-info` - WHY: dependency/cache artifacts must not create graph freshness or coverage evidence.
- #17 Python export metadata is best-effort: `__all__` wins when present, otherwise the leading-underscore convention marks module-level public names; file `exports[]` entries must include policy and supportedSymbol - WHY: Python has no enforced export boundary.
- #244 `python.syntax` resolves one concrete interpreter through the Python project resolver, executes that exact interpreter with an isolated versioned JSON compile protocol over sorted `.py`/`.pyi` after-state content, and reports ranged/provenanced compiler diagnostics plus fine-grained outcomes. Parser-success overrides and hand-written grammar heuristics are forbidden; malformed output, nonzero exits, signals, and timeouts fail closed - WHY: syntax truth, target-version truth, and tool provenance must come from the same compiler invocation without mutating the source repo.
- #246 makes `@the-open-engine/opcore-validation-python` the sole dynamic owner of `opcore.python.project-context.v1`: every Python target resolves against its nearest project boundary through an injected read/list/exists/realpath workspace view, smol-toml AST config/build metadata, exact interpreter/tool/build probes, and after-state content. Missing realpath evidence is ambiguous, deleted overlays cannot remain discovery markers, and declared constraints never become invented exact versions. Validation, status, scan, init/install preview, metrics, ASP, and installed execution must reuse the resulting project key, context fingerprint, outcome, and provenance; ASP workspace/config reads must remain host-callback-only. Static descriptors advertise only the schema, outcome vocabulary, read-only behavior, and no-install guarantee - WHY: root-scoped and duplicate project/environment discovery validates nested monorepo files with the wrong interpreter and makes surfaces disagree.
- #19 requires graph status to preserve real WAL checkpoint evidence from the latest pipeline summary and release gates to fail missing/fabricated WAL evidence - WHY: freshness and checkpoint pressure must remain host-visible provider facts.
- #19 treats `opcore graph serve` as the stdio/MCP hot-query replacement, not a Unix socket, with parallel independent serve sessions as the supported concurrency evidence.
- #19 keeps current external CRG receipts as non-implementation compatibility evidence only - WHY: old CRG remains a guardrail until downstream cutover issues consume the Opcore proof.
Expand Down
30 changes: 29 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"lint": "node scripts/check-workspace.mjs",
"pack:check": "node scripts/check-packages.mjs",
"provenance:check": "node scripts/check-provenance.mjs",
"python:resolver-matrix": "node scripts/check-python-resolver-matrix.mjs",
"release:hygiene": "node scripts/check-release-hygiene.mjs",
"rust:check": "npm run rust:fmt && npm run rust:clippy && npm run rust:test",
"rust:clippy": "cargo clippy --all-targets --all-features -- -D warnings",
Expand Down
12 changes: 12 additions & 0 deletions packages/asp-provider/src/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ export type OpcoreAspProviderManifest = {
protocolVersion: string;
capabilityFamilies: readonly ["check"];
checks: readonly string[];
pythonProjectContext: {
schemaId: "opcore.python.project-context.v1";
outcomes: readonly ["resolved", "degraded", "unsupported", "ambiguous"];
readOnly: true;
installs: false;
};
executable: {
packageName: string;
bin: "opcore-asp-provider";
Expand Down Expand Up @@ -102,6 +108,12 @@ export function createOpcoreAspProviderManifest(options: { packageRoot?: string;
protocolVersion: ASP_PROTOCOL_VERSION,
capabilityFamilies: ["check"],
checks: defaultAspProviderValidationCheckIds,
pythonProjectContext: {
schemaId: "opcore.python.project-context.v1",
outcomes: ["resolved", "degraded", "unsupported", "ambiguous"],
readOnly: true,
installs: false
},
executable: {
packageName: OPCORE_PROVIDER_PACKAGE,
bin: "opcore-asp-provider",
Expand Down
65 changes: 62 additions & 3 deletions packages/asp-provider/src/mapping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,13 @@ export function initializeResult(params: InitializeParams): JsonObject {
manifest: defaultAspProviderValidationManifest,
scopes: ["changeset", "paths"],
comparisons: [supportedComparison],
fixes: false
fixes: false,
pythonProjectContext: {
schemaId: "opcore.python.project-context.v1",
outcomes: ["resolved", "degraded", "unsupported", "ambiguous"],
readOnly: true,
installs: false
}
}
},
requestedPermissions: {
Expand Down Expand Up @@ -119,7 +125,7 @@ export async function evaluateChangeset(
};
});
validationResult = await timing.timeAsync("validation", () =>
createAspProviderValidationRunner(workspace.workspace).runValidation(request)
createAspProviderValidationRunner(workspace.workspace, workspace.pythonWorkspace).runValidation(request)
);
} catch (error) {
const validationFailure = errorAssessment({
Expand Down Expand Up @@ -429,6 +435,19 @@ function validationCoverageDegradations(result: ValidationResult, selectedIds: r
} {
const degraded: ProviderCoverageDegradation[] = [];
const unsupported: ProviderCoverageDegradation[] = [];
for (const context of result.pythonProjectContexts ?? []) {
if (context.outcome === "resolved") continue;
const reason = context.outcome === "unsupported" ? "unsupported" : "unavailable";
const entry = {
source: providerSource,
reason,
requirement: `python-project-context:${context.target}`,
detail: context.reasons.map((item) => `${item.code}: ${item.message}`).join("; ") ||
`Python project context ${context.outcome}`
};
degraded.push(entry);
if (reason === "unsupported") unsupported.push(entry);
}
for (const skipped of result.manifest?.skippedChecks ?? []) {
const entry = {
source: providerSource,
Expand Down Expand Up @@ -538,13 +557,53 @@ function validationEvidence(result: ValidationResult): JsonObject[] {
data.refusalCategory = result.refusal.category;
data.refusalMessage = result.refusal.message;
}
return [
const evidence: JsonObject[] = [
{
kind: "message",
message: "Opcore validation result mapped to ASP Core check assessment.",
data
}
];
for (const context of result.pythonProjectContexts ?? []) {
evidence.push({
kind: "python_project_context",
message: `Canonical Python project context for ${context.target}.`,
data: {
schemaId: context.schemaId,
target: context.target,
projectRoot: context.projectRoot,
projectKey: context.projectKey,
contextFingerprint: context.contextFingerprint,
outcome: context.outcome,
interpreter: context.interpreter === undefined
? null
: {
executable: context.interpreter.executable,
argv: [...context.interpreter.argv],
cwd: context.interpreter.cwd,
source: context.interpreter.source,
...(context.interpreter.version === undefined ? {} : { version: context.interpreter.version }),
...(context.interpreter.implementation === undefined ? {} : { implementation: context.interpreter.implementation }),
...(context.interpreter.platform === undefined ? {} : { platform: context.interpreter.platform }),
...(context.interpreter.architecture === undefined ? {} : { architecture: context.interpreter.architecture }),
...(context.interpreter.abi === undefined ? {} : { abi: context.interpreter.abi }),
...(context.interpreter.soabi === undefined ? {} : { soabi: context.interpreter.soabi })
},
tools: context.tools.map((tool) => ({
tool: tool.tool,
available: tool.available,
executable: tool.executable,
argv: [...tool.argv],
cwd: tool.cwd,
source: tool.source,
...(tool.configFile === undefined ? {} : { configFile: tool.configFile }),
...(tool.version === undefined ? {} : { version: tool.version })
})),
reasons: context.reasons.map((reason) => ({ code: reason.code, message: reason.message }))
}
});
}
return evidence;
}

function providerMetadata(): Assessment["provider"] {
Expand Down
2 changes: 2 additions & 0 deletions packages/asp-provider/src/node-shims.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ declare module "node:fs" {
}

declare module "node:fs/promises" {
export function access(path: string): Promise<void>;
export function realpath(path: string): Promise<string>;
export function readFile(path: string, encoding: "utf8"): Promise<string>;
export function chmod(path: string, mode: number): Promise<void>;
export function mkdir(path: string, options: { recursive: true }): Promise<void>;
Expand Down
20 changes: 12 additions & 8 deletions packages/asp-provider/src/validation-composition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
} from "@the-open-engine/opcore-validation";
import {
createBuiltInValidationChecks,
validationChecksForRepoPolicy
parseOpcoreRepoConfig
} from "@the-open-engine/opcore-validation-policy";
import {
graphProviderDetectChanges,
Expand All @@ -39,6 +39,7 @@ import {
graphProviderReviewContext,
graphProviderStatus
} from "@the-open-engine/opcore-graph";
import type { PythonProjectWorkspace } from "@the-open-engine/opcore-validation-python";

const aspProviderPolicyOptions = { clone: false } as const;

Expand All @@ -47,14 +48,15 @@ export const defaultAspProviderValidationChecks = createBuiltInValidationChecks(
export const defaultAspProviderValidationCheckIds = defaultAspProviderValidationChecks.map((check) => check.id);
export const defaultAspProviderValidationManifest = createValidationCheckManifest(defaultAspProviderValidationChecks);

export function createAspProviderValidationRunner(workspace: ValidationWorkspace): {
export function createAspProviderValidationRunner(workspace: ValidationWorkspace, pythonWorkspace: PythonProjectWorkspace): {
runValidation(request: ValidationRequest): Promise<ValidationResult>;
} {
return {
runValidation(request) {
async runValidation(request) {
const config = await readHostConfig(workspace, request);
return createValidationRunner({
workspace,
checks: validationChecksForAspRequest(request),
checks: createBuiltInValidationChecks(config, { ...aspProviderPolicyOptions, pythonWorkspace }),
graphProviderClient: createAspValidationGraphProviderClient()
}).runValidation(request);
}
Expand All @@ -67,10 +69,12 @@ export function selectedValidationChecks(checkIds?: readonly string[]): readonly
return defaultAspProviderValidationChecks.filter((check) => requested.has(check.id));
}

function validationChecksForAspRequest(request: ValidationRequest): readonly ValidationCheckDefinition[] {
const repoRoot = request.repo.repoRoot;
if (repoRoot === undefined) return defaultAspProviderValidationChecks;
return validationChecksForRepoPolicy(repoRoot, aspProviderPolicyOptions);
async function readHostConfig(workspace: ValidationWorkspace, request: ValidationRequest) {
const overlay = request.overlays.find((entry) => entry.path === ".opcore/config");
if (overlay?.action === "write") return parseOpcoreRepoConfig(overlay.content);
if (overlay?.action === "delete") return parseOpcoreRepoConfig(undefined);
const result = await workspace.readFile(".opcore/config");
return parseOpcoreRepoConfig(result.status === "found" ? result.content : undefined);
}

function createAspValidationGraphProviderClient(): ValidationGraphProviderClient {
Expand Down
Loading
Loading