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
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

### Fixed
- Core: preserve `verification.on_fail` through compilation (#64, PR #71). The recovery action (`retry`/`escalate`/`skip`/`abort`/`revise`) was silently dropped at the compile boundary. Now carried as a first-class `CompiledVerification` on a singular `CompiledStep.verification`; the gate-validator return shape got its own name `QualityGateResult`. Parametrised across all five `OnFailAction` values plus the null case.
- Core: forward `quality_gates.pre_output[].on_fail` into compiled output via `QualityGateResult.onFail` (#72, PR #82). The pre_output gate recovery action was dropped at the compile boundary; now surfaced on the failing result and omitted when the author omits it. Parametrised across all five `OnFailAction` values plus the absent case.
- MCP: HTTP transport responds with a structured 500 on handler error instead of hanging until socket timeout (#66, PR #79). `headersSent`/`writableEnded` guards prevent a double-write.
- Build: cli/mcp `postbuild` schema copy now fails loud instead of `|| true` swallowing both copy attempts (#67, PR #78). The bundled core validator reads its own `dist/schema.json` at runtime, so the copy is required.
- Fixtures: `run-fixtures.mjs` fails loud (exit 2) on non-ENOENT `readdir` errors instead of treating every error as "directory not found" and reporting partial success as full success (#68, PR #80).
Expand All @@ -20,10 +21,17 @@

### Changed
- Docs: replace stale `ROADMAP.md` with `STATUS.md` as the user-facing status doc; ignore contributor PDFs (#77).
- Core: the dry-run executor now reads a step's verification from `CompiledStep.verification` instead of the un-compiled spec (#73, PR #84). Consumer side of #64; behaviour preserved (the compiler maps `on_fail_message` to `message`).
- Core/LangGraph: the executor reflects `CompiledStep.executionPlan` onto `StepTrace.executionPlan`, and the LangGraph adapter reflects it onto `StateGraphNode.metadata.executionPlan` (#75, PRs #85 and #86). Consumer side of #65; consume-and-reflect only, no concurrent scheduler.

### Tests
- CLI: pin `toCanonical` failure modes for malformed frontmatter, distinguishing throw (malformed / tab YAML) from null (unbalanced delimiters, no frontmatter) (#69, PR #81).

## [1.5.1] - 2026-07-24

### Fixed
- Core: `schema.ts`, `parser.ts`, `validator.ts` load `gray-matter`/`ajv-formats`/`schema.json` via static ESM imports instead of `createRequire`/`readFileSync`. The `createRequire` path broke under single-file bundlers (bun `--compile`); consumers were carrying this as a local patch. Matches the fix already shipped in `@marchese-md/core`.

Comment on lines +30 to +34

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Prune the changelog to the required 20-entry rolling window.

The file now contains substantially more than 20 notable entries, so adding [1.5.1] without removing older entries violates the repository’s changelog policy. Retain the latest 20 entries and move older history to git history or a separate archive.

As per coding guidelines, CHANGELOG.md maintains a rolling log of the last 20 entries.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CHANGELOG.md` around lines 30 - 34, Prune CHANGELOG.md to retain only the
latest 20 release entries, including [1.5.1], and remove older entries beyond
that rolling window without changing the remaining entry content.

Source: Coding guidelines

## [1.5.0] - 2026-05-15

### Added
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@logic-md/core",
"version": "1.5.0",
"version": "1.5.1",
"description": "Parser, validator, expression engine, DAG resolver, and reasoning compiler for the LOGIC.md specification",
"type": "module",
"main": "./dist/index.js",
Expand Down
5 changes: 1 addition & 4 deletions packages/core/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,9 @@
// only extracts and casts.
// =============================================================================

import { createRequire } from "node:module";
import matter from "gray-matter";
import type { LogicSpec } from "./types.js";

const require = createRequire(import.meta.url);
const matter = require("gray-matter") as typeof import("gray-matter");

// -----------------------------------------------------------------------------
// Result Types
// -----------------------------------------------------------------------------
Expand Down
32 changes: 11 additions & 21 deletions packages/core/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,23 @@
// ValidateFunction for runtime validation of LogicSpec objects.
// =============================================================================

import { readFileSync } from "node:fs";
import { createRequire } from "node:module";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { Ajv, type ValidateFunction } from "ajv";
import addFormatsMod from "ajv-formats";
import schemaJson from "./schema.json" with { type: "json" };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

rg -n \
  --glob 'package.json' \
  --glob '*.yml' \
  --glob '*.yaml' \
  --glob '.nvmrc' \
  --glob '.node-version' \
  --glob '.tool-versions' \
  '"type"|"engines"|node-version|NODE_VERSION|bun' .

rg -n 'schema\.json|with \{ type: "json" \}' packages/core

Repository: SingularityAI-Dev/logic-md

Length of output: 1279


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '--- package engines ---\n'
python3 - <<'PY'
import json, pathlib
for p in sorted(pathlib.Path('.').rglob('package.json')):
    if any(s in p.parts for s in ('node_modules', '.git')):
        continue
    try:
        data=json.loads(p.read_text())
    except Exception as e:
        continue
    fields={k:data.get(k) for k in ('type','engines') if k in data}
    if fields:
        print(f'{p}: {fields}')
PY

printf '\n--- core package metadata relevant to engines/typescript/bundlers ---\n'
sed -n '1,120p' packages/core/package.json

printf '\n--- TypeScript configs ---\n'
for f in package.json tsconfig.json packages/*/tsconfig.json; do
  [ -f "$f" ] && { echo "===== $f ====="; sed -n '1,160p' "$f"; }
done

printf '\n--- CI/workflow node versions and package manager hints ---\n'
rg -n --glob '*.yml' --glob '*.yaml' --glob 'package-lock.json' --glob 'pnpm-lock.yaml' --glob 'bun.lockb' --glob 'bun.lock' 'node:|node-version|npm|pnpm|corepack|bun|engines|types' .github package.json packages/core/package.json packages/cli/package.json packages/mcp/package.json || true

printf '\n--- Read packages/core/schema.ts relevant lines ---\n'
sed -n '1,80p' packages/core/schema.ts

printf '\n--- static check: JSON import attribute usage ---\n'
python3 - <<'PY'
from pathlib import Path
for p in Path('.').rglob('*.ts'):
    if not p.match('*schema.ts'):
        continue
    s=p.read_text()
    if 'with { type: "json" }' in s or "with { type: 'json' }" in s:
        print(p)
PY

Repository: SingularityAI-Dev/logic-md

Length of output: 255


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- package engines ---'
python3 - <<'PY'
import json, pathlib
for p in sorted(pathlib.Path('.').rglob('package.json')):
    if any(s in p.parts for s in ('node_modules', '.git')):
        continue
    try:
        data=json.loads(p.read_text())
    except Exception as e:
        continue
    fields={k:data.get(k) for k in ('type','engines') if k in data}
    if fields:
        print(f"{p}: {fields}")
PY

printf '%s\n' ''
printf '%s\n' '--- core package metadata relevant to engines/typescript/bundlers ---'
sed -n '1,120p' packages/core/package.json

printf '%s\n' ''
printf '%s\n' '--- TypeScript configs ---'
for f in package.json tsconfig.json packages/*/tsconfig.json; do
  if [ -f "$f" ]; then
    echo "===== $f ====="
    sed -n '1,160p' "$f"
  fi
done

printf '%s\n' ''
printf '%s\n' '--- CI/workflow node versions and package manager hints ---'
rg -n --glob '*.yml' --glob '*.yaml' --glob 'package-lock.json' --glob 'pnpm-lock.yaml' --glob 'bun.lockb' --glob 'bun.lock' 'node:|node-version|npm|pnpm|corepack|bun|engines|types' .github package.json packages/core/package.json packages/cli/package.json packages/mcp/package.json || true

printf '%s\n' ''
printf '%s\n' '--- Read packages/core/schema.ts relevant lines ---'
sed -n '1,80p' packages/core/schema.ts

printf '%s\n' ''
printf '%s\n' '--- static check: JSON import attribute usage ---'
python3 - <<'PY'
from pathlib import Path
for p in Path('.').rglob('*.ts'):
    if not p.name.endswith('schema.ts'):
        continue
    s=p.read_text()
    if 'with { type: "json" }' in s or "with { type: 'json' }" in s:
        print(p)
PY

Repository: SingularityAI-Dev/logic-md

Length of output: 8005


Raise the @logic-md/core Node engine floor to the JSON import attribute baseline.

packages/core/tsconfig.json is under the root tsconfig with module: "nodenext", and packages/core/package.json only declares node: ">=18", but this import attribute requires Node support not guaranteed by that lower bound. If the package must support Node 18, replace the JSON import attribute with an engine-guaranteed/ bundled alternative, or bump @logic-md/core engines to Node 20+.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/schema.ts` at line 10, Update the `@logic-md/core` compatibility
contract for the JSON import attribute in schema.ts: either replace the
import-attribute usage with an approach supported by the declared Node 18 floor,
or raise the package.json Node engine requirement to the minimum version that
guarantees it (Node 20+). Keep the tsconfig module setting and schema loading
behavior consistent.

Source: MCP tools

import type { LogicSpec } from "./types.js";

const require = createRequire(import.meta.url);
// ajv-formats is a CJS module with `export default` that doesn't resolve
// correctly under verbatimModuleSyntax + nodenext. Use createRequire instead.
const addFormats = require("ajv-formats") as {
default: (ajv: Ajv) => Ajv;
};

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// ajv-formats is CJS; unwrap the interop default so this works under both
// Node ESM and single-file bundlers.
const addFormats = ((addFormatsMod as { default?: unknown }).default ?? addFormatsMod) as (
ajv: Ajv,
) => Ajv;

/**
* Reads and parses the embedded JSON Schema from disk.
* Uses `import.meta.url` for path resolution so it works regardless
* of the caller's working directory.
* Returns the embedded JSON Schema (statically imported so it survives
* bundling into single-file executables).
*/
export function getSchema(): Record<string, unknown> {
const schemaPath = join(__dirname, "schema.json");
const raw = readFileSync(schemaPath, "utf8");
return JSON.parse(raw) as Record<string, unknown>;
return structuredClone(schemaJson) as Record<string, unknown>;
}

/** Cached validator instance (module-level singleton) */
Expand All @@ -52,8 +43,7 @@ export function createValidator(): ValidateFunction<LogicSpec> {
}

const ajv = new Ajv({ allErrors: true, strict: true });
const applyFormats = addFormats.default ?? addFormats;
(applyFormats as (ajv: Ajv) => Ajv)(ajv);
addFormats(ajv);

const schema = getSchema();
cachedValidator = ajv.compile<LogicSpec>(schema);
Expand Down
2 changes: 1 addition & 1 deletion packages/core/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
"rootDir": ".",
"composite": true
},
"include": ["*.ts", "**/*.ts"],
"include": ["*.ts", "**/*.ts", "schema.json"],
"exclude": ["dist", "node_modules", "**/*.test.ts"]
}
5 changes: 1 addition & 4 deletions packages/core/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,11 @@
// and maps ajv error paths back to YAML source line numbers.
// =============================================================================

import { createRequire } from "node:module";
import matter from "gray-matter";
import { type Document, LineCounter, type Node, parseDocument } from "yaml";
import { createValidator } from "./schema.js";
import type { LogicSpec, ValidationError, ValidationResult } from "./types.js";

const require = createRequire(import.meta.url);
const matter = require("gray-matter") as typeof import("gray-matter");

/**
* Offset added to YAML line numbers to account for the opening `---`
* delimiter line that gray-matter strips before returning `result.matter`.
Expand Down
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"declarationMap": true,
"sourceMap": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
Expand Down