Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
53 changes: 33 additions & 20 deletions .agents/skills/jacobian-math/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,22 @@ description: Use Jacobian for specialized exact mathematics, including matrix de
In Codex Code Mode, call the nested methods
`tools.mcp__jacobian__math_find(...)` and
`tools.mcp__jacobian__math_run(...)` directly. Do not enumerate, filter, or
print `ALL_TOOLS` merely to locate them; that needlessly adds every matching
tool description to the model context. Return only the typed projection when
available, for example:
print `ALL_TOOLS`; that loads matching tool descriptions into context. Return
only the typed projection when available:

```js
const r = await tools.mcp__jacobian__math_find({query: "...", limit: 3});
text(r.structuredContent ?? r);
```

Call `math.run` directly when the requested local outcome exactly matches one of
these stable built-in contracts; replace the example values but preserve the
shown JSON types:
Use Jacobian for each requested exact mathematical outcome, even when small or
shell-solvable. Route multiple requested outcomes separately. Prefer a
capability matching the requested operation over a generic arithmetic substep
that yields the same scalar; composing already-known supporting operations remains allowed when clearer.
Comment thread
morluto marked this conversation as resolved.
Do not call Jacobian for definitions, formatting, or non-execution tasks.

Call `math.run` directly when the requested local outcome exactly matches these
stable built-in contracts; replace examples but preserve JSON types:

- `integer.compute.gcd`, `integer.compute.lcm`, or
`integer.compute.extended_gcd` in `EXPLORE` mode:
Expand All @@ -36,22 +40,31 @@ shown JSON types:
`{"determinant_uri":"<determinant_uri from compute output>"}`.

For other outcomes or unfamiliar payloads, use `math.find` with a specific
plain-language outcome and any domain or mode already implied by the task; a
capability ID is not required. Request only as many matches as are useful
because lower `limit` values reduce context. Inspect the `CONTRACT` view only
when its typed schema is needed to construct the `math.run` payload. A discovery
card's validated `invocation_example`, or its required top-level fields when no
example is available, may already provide a sufficient payload shape.
plain-language outcome and implied domain or mode; no capability ID is required.
Use low `limit` values. Inspect `CONTRACT` only when the typed schema is needed.
A card's `invocation_example`, or required top-level fields, may be enough.

Do not add a discovery domain filter unless its exact installed spelling is
known. When discovery exposes recovery paths, follow those fields (for example,
`remove_unknown_domain_filter`, `remove_filters`, or `reformulate_query`) before
treating absence as final. After invalid input, correct the reported constraint
and retry within the task resource bounds as further errors appear. If one
provider is unavailable, continue with other installed routes that can produce
the outcome. Treat timeouts as non-conclusions. Accept only a completed result
whose scope covers the input, and carry forward the smallest decisive value,
witness, status, assurance, completeness, and open obligations; preserve artifact
refs, including verification record URIs.

Keep representation, decomposition, composition, iteration, verification
timing, and stopping decisions agent-owned. Treat timeouts, errors, incomplete
searches, and missing witnesses as non-conclusions.

When independent checking is requested, calculations or programs authored by
the same model are not independent checker evidence. Use an installed `VERIFY`
capability when available. An artifact URI or checker-result summary is not a
task-local verification-record file: never reconstruct or paraphrase such a
record from the returned fields. Claim `VERIFIED` only when the result has
assurance level `VERIFIED`, the exact record bytes are available, and any
required task authorization and bindings are preserved. Otherwise use a lower
assurance permitted by the task.
When independent checking is requested, model-authored calculations or programs
are not independent evidence. Use installed `VERIFY` when available. An artifact
URI or checker summary is not a task-local verification-record file: never
reconstruct or paraphrase such a record from returned fields. Claim `VERIFIED`
only when the result has assurance level `VERIFIED`, exact record bytes, and
required task authorization and bindings are preserved; otherwise use lower
task-permitted assurance. Verification is bound to the exact checked claim: do
not transfer `VERIFIED` from an input, premise, factorization, or related
artifact to a model-derived conclusion, which needs its own checker-bound record.
53 changes: 33 additions & 20 deletions npm/skills/jacobian-math/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,22 @@ description: Use Jacobian for specialized exact mathematics, including matrix de
In Codex Code Mode, call the nested methods
`tools.mcp__jacobian__math_find(...)` and
`tools.mcp__jacobian__math_run(...)` directly. Do not enumerate, filter, or
print `ALL_TOOLS` merely to locate them; that needlessly adds every matching
tool description to the model context. Return only the typed projection when
available, for example:
print `ALL_TOOLS`; that loads matching tool descriptions into context. Return
only the typed projection when available:

```js
const r = await tools.mcp__jacobian__math_find({query: "...", limit: 3});
text(r.structuredContent ?? r);
```

Call `math.run` directly when the requested local outcome exactly matches one of
these stable built-in contracts; replace the example values but preserve the
shown JSON types:
Use Jacobian for each requested exact mathematical outcome, even when small or
shell-solvable. Route multiple requested outcomes separately. Prefer a
capability matching the requested operation over a generic arithmetic substep
that yields the same scalar; composing already-known supporting operations remains allowed when clearer.
Do not call Jacobian for definitions, formatting, or non-execution tasks.

Call `math.run` directly when the requested local outcome exactly matches these
stable built-in contracts; replace examples but preserve JSON types:

- `integer.compute.gcd`, `integer.compute.lcm`, or
`integer.compute.extended_gcd` in `EXPLORE` mode:
Expand All @@ -36,22 +40,31 @@ shown JSON types:
`{"determinant_uri":"<determinant_uri from compute output>"}`.

For other outcomes or unfamiliar payloads, use `math.find` with a specific
plain-language outcome and any domain or mode already implied by the task; a
capability ID is not required. Request only as many matches as are useful
because lower `limit` values reduce context. Inspect the `CONTRACT` view only
when its typed schema is needed to construct the `math.run` payload. A discovery
card's validated `invocation_example`, or its required top-level fields when no
example is available, may already provide a sufficient payload shape.
plain-language outcome and implied domain or mode; no capability ID is required.
Use low `limit` values. Inspect `CONTRACT` only when the typed schema is needed.
A card's `invocation_example`, or required top-level fields, may be enough.

Do not add a discovery domain filter unless its exact installed spelling is
known. When discovery exposes recovery paths, follow those fields (for example,
`remove_unknown_domain_filter`, `remove_filters`, or `reformulate_query`) before
treating absence as final. After invalid input, correct the reported constraint
and retry within the task resource bounds as further errors appear. If one
provider is unavailable, continue with other installed routes that can produce
the outcome. Treat timeouts as non-conclusions. Accept only a completed result
whose scope covers the input, and carry forward the smallest decisive value,
witness, status, assurance, completeness, and open obligations; preserve artifact
refs, including verification record URIs.

Keep representation, decomposition, composition, iteration, verification
timing, and stopping decisions agent-owned. Treat timeouts, errors, incomplete
searches, and missing witnesses as non-conclusions.

When independent checking is requested, calculations or programs authored by
the same model are not independent checker evidence. Use an installed `VERIFY`
capability when available. An artifact URI or checker-result summary is not a
task-local verification-record file: never reconstruct or paraphrase such a
record from the returned fields. Claim `VERIFIED` only when the result has
assurance level `VERIFIED`, the exact record bytes are available, and any
required task authorization and bindings are preserved. Otherwise use a lower
assurance permitted by the task.
When independent checking is requested, model-authored calculations or programs
are not independent evidence. Use installed `VERIFY` when available. An artifact
URI or checker summary is not a task-local verification-record file: never
reconstruct or paraphrase such a record from returned fields. Claim `VERIFIED`
only when the result has assurance level `VERIFIED`, exact record bytes, and
required task authorization and bindings are preserved; otherwise use lower
task-permitted assurance. Verification is bound to the exact checked claim: do
not transfer `VERIFIED` from an input, premise, factorization, or related
artifact to a model-derived conclusion, which needs its own checker-bound record.
13 changes: 12 additions & 1 deletion src/jacobian/adapters/mcp/guidance.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@
"mathematical conclusion, and assurance separate. No descriptor match, timeout, "
"bounded or exhausted search, or failure to find a witness is a mathematical "
"conclusion. Only assurance level VERIFIED with a local verification record is "
"verified. Read jacobian://instructions for the complete operating guide."
"verified. A verification record for an input, premise, factorization, or related "
"artifact does not verify a model-derived conclusion; the record must be bound to "
"the exact final claim. Read jacobian://instructions for the complete operating "
"guide."
)

MATH_FIND_DESCRIPTION = """\
Expand Down Expand Up @@ -80,6 +83,10 @@
not widen that scope. Follow returned `artifact://` references when durable evidence
or a size-separated result is provided.

A verification record is claim-bound. Verification of an input, premise,
factorization, or related artifact does not promote a conclusion derived by the model
to `VERIFIED`; the checker record must bind the exact final claim.

Examples:
- `{"capability_id":"integer.compute.gcd","mode":"EXPLORE","payload":{"left":"84","right":"30"}}`
- `{"capability_id":"polynomial.identity.verify","mode":"VERIFY","payload":{"variables":["x"],"left":{"terms":[]},"right":{"terms":[]}}}`
Expand Down Expand Up @@ -140,6 +147,10 @@
format, and checker identity. Only assurance level `VERIFIED` with a local
verification record is verified.

Verification does not transfer across model-authored deductions. A record accepting
premises, inputs, factorizations, or related artifacts does not verify a derived
conclusion unless a checker record is bound to that exact conclusion.

Execution status is not a mathematical conclusion. `COMPLETED` bounded execution may
still have partial or unknown completeness and open obligations. A timeout,
cancellation, error, incomplete enumeration, or failure to find a witness is a
Expand Down
10 changes: 8 additions & 2 deletions src/jacobian/capability_dispatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,15 +146,21 @@ def invoke(self: Any, request: CapabilityRequest) -> CapabilityResult:
else json_value_type(request.input)
),
hint=(
"Correct the reported field. The exact required and missing "
"top-level fields are included in diagnostic details."
"Correct the reported field. The exact violated constraint "
"and any required or missing top-level fields are included "
"in diagnostic details."
),
details={
"required_fields": descriptor.input_schema.get("required", []),
"missing_fields": sorted(
set(descriptor.input_schema.get("required", []))
- set(request.input)
),
**(
exc.details
if isinstance(exc, PayloadValidationError)
else {}
),
},
),
)
Expand Down
4 changes: 4 additions & 0 deletions src/jacobian/capability_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

from __future__ import annotations

from typing import Any

from jacobian.contracts.capabilities import CapabilityDiagnostic


Expand All @@ -19,11 +21,13 @@ def __init__(
path: str,
actual_type: str,
expected: str,
details: dict[str, Any] | None = None,
) -> None:
super().__init__(message)
self.path = path
self.actual_type = actual_type
self.expected = expected
self.details = details or {}


class CapabilityDiscoveryCursorError(ValueError):
Expand Down
53 changes: 53 additions & 0 deletions src/jacobian/capability_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ def validate_payload(
path=location,
actual_type=json_value_type(first.instance),
expected=schema_expectation(first),
details=schema_violation_details(first),
)
return normalized

Expand Down Expand Up @@ -95,13 +96,65 @@ def schema_expectation(error: JsonSchemaValidationError) -> str:
if isinstance(expected, list):
return "JSON type " + " or ".join(str(item) for item in expected)
return f"JSON type {expected}"
constraint_labels = {
"minimum": "a number greater than or equal to",
"exclusiveMinimum": "a number greater than",
"maximum": "a number less than or equal to",
"exclusiveMaximum": "a number less than",
"minLength": "a string with minimum length",
"maxLength": "a string with maximum length",
"minItems": "an array with minimum length",
"maxItems": "an array with maximum length",
"multipleOf": "a number that is a multiple of",
"pattern": "a string matching pattern",
}
label = constraint_labels.get(str(error.validator))
if label is not None:
rendered = json.dumps(error.validator_value, ensure_ascii=False)
# Truncate to stay within CapabilityDiagnostic.expected's 1024-char limit
max_value = 1024 - len(label) - 1
if len(rendered) > max_value:
rendered = rendered[: max(0, max_value - 3)] + "..."
return f"{label} {rendered}"
return "input matching the capability descriptor JSON Schema"


def schema_violation_details(error: JsonSchemaValidationError) -> dict[str, object]:
"""Return the exact public schema constraint that rejected one value."""

details: dict[str, object] = {"validator": str(error.validator)}
if error.validator in {
Comment thread
morluto marked this conversation as resolved.
"minimum",
"exclusiveMinimum",
"maximum",
"exclusiveMaximum",
"minLength",
"maxLength",
"minItems",
"maxItems",
"multipleOf",
"pattern",
"enum",
"const",
"type",
}:
constraint_value = error.validator_value
if isinstance(constraint_value, str):
if len(constraint_value) > 1024:
constraint_value = constraint_value[:1021] + "..."
else:
rendered = json.dumps(constraint_value, ensure_ascii=False)
if len(rendered) > 1024:
constraint_value = rendered[:1021] + "..."
details["constraint"] = constraint_value
return details


__all__ = [
"compiled_validator",
"json_value_type",
"schema_expectation",
"schema_violation_details",
"validate_payload",
"validator",
]
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ def test_invalid_capability_input_does_not_echo_payload(
assert diagnostic.details == {
"required_fields": ["value"],
"missing_fields": [],
"validator": "type",
"constraint": "integer",
}
assert "fixture-secret-value" not in repr(diagnostic)

Expand Down
45 changes: 45 additions & 0 deletions tests/unit/contracts/test_capability_validation_diagnostics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from __future__ import annotations

import pytest

from jacobian.capability_errors import PayloadValidationError
from jacobian.capability_validation import validate_payload


def test_payload_validation_reports_exact_maximum_constraint() -> None:
with pytest.raises(PayloadValidationError) as error:
validate_payload(
{
"type": "object",
"properties": {"n": {"type": "integer", "maximum": 1000}},
"required": ["n"],
"additionalProperties": False,
},
{"n": 2310},
)

assert error.value.path == "n"
assert error.value.actual_type == "integer"
assert error.value.expected == "a number less than or equal to 1000"
assert error.value.details == {"validator": "maximum", "constraint": 1000}


def test_payload_validation_bounds_long_pattern_text() -> None:
"""A pattern longer than 1024 chars must not exceed the diagnostic limit."""

long_pattern = "a" * 2000
with pytest.raises(PayloadValidationError) as error:
validate_payload(
{
"type": "object",
"properties": {"s": {"type": "string", "pattern": long_pattern}},
"required": ["s"],
"additionalProperties": False,
},
{"s": "b"},
)

assert error.value.expected is not None
assert len(error.value.expected) <= 1024
assert error.value.expected.startswith("a string matching pattern ")
assert error.value.expected.endswith("...")
6 changes: 6 additions & 0 deletions tests/unit/tooling/test_mcp_tool_surface.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,9 @@ def test_server_instructions_front_load_implicit_activation_signal() -> None:
def test_server_instructions_allow_known_contracts_to_run_directly() -> None:
assert "exact installed capability ID" in SERVER_INSTRUCTIONS
assert "math.run may execute a known contract directly" in SERVER_INSTRUCTIONS


def test_guidance_rejects_verification_transfer_to_derived_claims() -> None:
combined = "\n".join((SERVER_INSTRUCTIONS, MATH_RUN_DESCRIPTION))
assert "does not verify a model-derived conclusion" in combined
assert "record must be bound to the exact final claim" in combined
Loading