Skip to main content

API Reference — mycontext.rac

All RaC functions are LLM-native: every call routes through an LLM using your own provider key. There is no offline/deterministic fallback. A provider key is required for all generation, validation, and trace functions.

from mycontext.rac import (
# Step 0 — Intent assessment
assess, IntakeBrief,
# Step 1 — Product requirements (the what & why)
product,
# Step 2 — Technical requirements (the how)
technical,
# Step 3 — Review & alignment
validate, trace, format_report,
# Step 4 — Render for coding agents
project, TARGETS,
# Utilities
to_yaml, score_output,
# Cognitive-pattern grounding
analyze, format_brief, select_patterns,
# Structured-output contract (Pydantic)
ProductSpec, TechnicalSpec,
parse_product, parse_technical,
check_product_integrity, check_technical_integrity,
normalize_product, normalize_technical,
# Error type
LLMUnavailable,
)

Step 0 — Intake

assess

assess(
text: str,
*,
provider: str = "openai",
model: str | None = None,
) -> IntakeBrief

Assess a raw intent and return a structured IntakeBrief. Fully LLM-native. text may be a sentence, a paragraph, or the full contents of a .txt / .md file.

The LLM analyses every facet of the intent — system name, task taxonomy, risk level, must-never lines, tool candidates, stakeholders, volume, constraints, open questions — and grounds the analysis in a set of dynamically selected cognitive patterns.

ParameterTypeDefaultDescription
textstrrequiredRaw intent (inline text or file contents).
providerstr"openai"LLM provider (openai, anthropic, gemini).
modelstr | NoneNoneModel name. None uses the provider's default (gpt-4o-mini for OpenAI).

Returns: IntakeBrief

Raises: LLMUnavailable if no provider key is configured.

Example:

import os
os.environ["OPENAI_API_KEY"] = "sk-..."

from mycontext.rac import assess

brief = assess(
"A support bot that drafts replies to billing emails "
"but never sends refunds without human approval."
)
print(brief.system_name) # e.g. "billing-support-bot"
print(brief.kind) # "agent"
print(brief.to_markdown()) # human-readable brief
print(brief.to_json()) # JSON string

IntakeBrief

@dataclass
class IntakeBrief:
intent: str # the original input text
data: dict[str, Any] # full LLM-authored brief dict
patterns_used: list[str] # cognitive patterns that grounded the analysis

@property
def system_name(self) -> str # kebab-case system name (e.g. "billing-support-bot")
@property
def kind(self) -> str # "agent" | "rag" | "multi_agent" | "service"

def to_dict(self) -> dict # raw brief as a dict
def to_json(self) -> str # JSON string (pretty-printed)
def to_markdown(self) -> str # human/coding-agent-friendly Markdown

The data dict contains the full structured brief, including:

KeyTypeDescription
system_namestrInferred kebab-case system name.
kindstrSystem type: agent | rag | multi_agent | service.
summarystrPlain-language description of what the system does.
primary_capabilitieslist[str]Core capabilities inferred from the intent.
task_typeslist[dict]Each task with name, example, freq_hint, risk, handling.
out_of_scope_categorieslist[str]Named refusal categories.
must_neverlist[str]Hard safety boundaries.
tools_or_actionslist[dict]Candidate tools with reversible, worst_case, suggested_policy.
stakeholderslist[dict]Stakeholders and what they sign off on.
constraintsdicthitl, pii, money_actions, budget_per_task_usd.
riskslist[str]Pre-mortem failure modes.
assumptionslist[str]Conditions the system takes for granted.
open_questionslist[dict]{id, question, why_it_matters, blocking} — gaps to resolve before ratifying.
recommended_patternslist[str]Cognitive patterns worth running for deeper analysis.
brief_markdownstrFull Markdown rendering of the brief.

Step 1 — Product requirements

product

product(
intent: str | IntakeBrief,
*,
provider: str = "openai",
model: str | None = None,
brief: IntakeBrief | None = None,
) -> dict

Generate a complete product requirements spec (the what & why) from a natural-language intent or a pre-computed IntakeBrief. The LLM decides how many tasks, rubric criteria, actions, safety requirements, and release gates the system warrants — there is no fixed skeleton. A Pydantic structured-output contract enforces the typed-ID grammar and referential integrity, with a self-repair loop.

ParameterTypeDefaultDescription
intentstr | IntakeBriefrequiredRaw intent text, or a pre-computed IntakeBrief from assess().
providerstr"openai"LLM provider.
modelstr | NoneNoneModel name; None uses the provider default.
briefIntakeBrief | NoneNoneDeprecated keyword alias for passing an IntakeBrief as intent.

Returns: dict — a product spec with meta.spec_type == "product_requirements".

Raises: LLMUnavailable if no key; RuntimeError if the LLM returns empty sections after the repair loop.

Examples:

from mycontext.rac import assess, product, to_yaml

# From raw text
prod = product(
"Brightcart support bot that drafts billing replies; "
"refunds need human approval; never leak customer data."
)
print(prod["meta"]["system_name"]) # e.g. "brightcart-support-bot"
print(prod["meta"]["spec_type"]) # "product_requirements"
print(list(prod["tasks"].keys())) # e.g. ["T1", "T2", "T_oos"]

# From a pre-computed IntakeBrief (reuse the assessment)
brief = assess("...", provider="openai", model="gpt-4o")
prod = product(brief, provider="openai", model="gpt-4o")

# Save to file
with open("product.yaml", "w") as f:
f.write(to_yaml(prod))

Product spec structure:

KeyTypeDescription
metadictsystem_name, spec_type, kind, status, intent, generated_by, generated_by_model, informed_by.
tasksdictT1, T2, …, mandatory T_oos. Each has name, example, risk, handling, rubric, no_draft, freq_hint.
rubricsdictR-T1, R-T2, …, R-T_oos. Each has criteria: [{id, anchor, grader, text}]. Graders: code | judge | human.
actionslist[{id: "A-1", policy: "auto|approve|forbidden", reversible, worst_case, tools}].
safetylist[{id: "P1", incident, requirement, eval: {dataset, assert, hard_gate}}].
datasetsdictSize, contamination rule, flywheel strategy.
baselinesdicthuman and bare_model baselines for comparison.
budgetsdictCost/latency budgets per task.
gatesdict{items: [{id: "G-1", scope, metric, threshold, hard_gate}], on_failure}.
monitoringdictDrift alarms, HITL stats.
assumptionslist[str]Conditions this spec takes for granted.
open_questionslist[dict]{id: "OQ-01", question, affects, blocking, status} — gaps to resolve.

Typed-ID grammar:

ID typePatternExample
TaskT<n> or T_oosT1, T_oos
RubricR-<task>R-T2, R-T_oos
Rubric criterionR-<task>.<n>R-T2.3
ActionA-<n>A-4
SafetyP<n>P1
GateG-<n>G-2b
Open questionOQ-<nn>OQ-01

Step 2 — Technical requirements

technical

technical(
text: str | None = None,
*,
product: dict | str | None = None,
frontier: bool = False,
provider: str = "openai",
model: str | None = None,
) -> dict

Generate a complete technical requirements spec (the how) covering all 24 sections of the Fable agentic-engineering template. The LLM authors every section in two focused passes (12 sections each) to avoid token-budget truncation.

Pass product=<spec> for traceable controls whose serves lists reference the product's task/action/safety/gate IDs. Pass text to bootstrap from intent when no product spec exists yet.

ParameterTypeDefaultDescription
textstr | NoneNoneNatural-language intent. Required if product is not given.
productdict | str | NoneNoneProduct spec dict or a YAML/JSON string. When a string is passed it is parsed automatically with yaml.safe_load.
frontierboolFalseInclude detailed content for model_layer_advanced, agent_rl, and frontier_capabilities (Tier-3 sections).
providerstr"openai"LLM provider.
modelstr | NoneNoneModel name. None uses the provider default.

Exactly one of text or product must be supplied; passing neither raises ValueError.

Returns: dict — a technical spec with meta.spec_type == "technical_requirements".

Raises: LLMUnavailable if no key; RuntimeError if the LLM returns empty Tier-1 sections after the repair loop.

Examples:

from mycontext.rac import product, technical, to_yaml

prod = product("Billing support bot; refunds need approval.")

# Rich path — traceable controls (recommended)
tech = technical(product=prod, provider="openai", model="gpt-4o")
print(tech["meta"]["source"]) # "product_requirements"
print(tech["architecture"]["pattern"]) # e.g. "typed_stateful_orchestrator"

# Bootstrap path — from text only
tech = technical(
"A RAG assistant that answers policy questions; never invents answers.",
provider="openai",
)
print(tech["meta"]["source"]) # "natural_language"

# Frontier mode — add fine-tune / RL / computer-use layer
tech = technical(product=prod, frontier=True, model="gpt-4o")

# Accepts a YAML string (e.g. loaded from disk as str)
with open("product.yaml") as f:
prod_yaml = f.read()
tech = technical(product=prod_yaml)

# Save
with open("technical.yaml", "w") as f:
f.write(to_yaml(tech))

Technical spec — 24 sections:

The spec has three tiers. Tier-1 sections are required for every production agent. Tier-2 are common operational concerns. Tier-3 are advanced model-layer topics (usually marked not_applicable: true unless frontier=True).

TierSections
Tier 1 — Functionalidentity, ownership, architecture, autonomy, memory, guardrails, tools
Tier 1 — Non-functionalquality_gates, operational_constraints, cost, deployment, observability, security, failure_behavior
Tier 2incident_response, audit, compliance, governance, promptops, caching, data_engineering, continuous_improvement
Tier 3model_layer_advanced, agent_rl, frontier_capabilities, lifecycle

Each section may be marked {not_applicable: true, reason: "..."} if it genuinely does not apply — absence is never "decided". Controls that implement a product requirement carry a serves: ["T1", "P1", "A-2"] list for traceability.

meta keys in technical specs:

KeyDescription
system_nameCanonical name (always taken from the product spec, never the LLM's placeholder).
spec_type"technical_requirements"
for_productSame as system_name — the product this technical spec implements.
kindAgent kind (agent, rag, etc.).
source"product_requirements" or "natural_language".
intentThe original intent text.
product_idsList of all product IDs available for traceability (only present when product= was supplied and non-empty).
informed_byCognitive patterns used to ground the generation.
generated_by"mycontext-ai <version> technical-architect"
generated_by_modelModel that authored the spec.

Step 3 — Review & alignment

validate

validate(
doc: dict,
*,
provider: str = "openai",
model: str | None = None,
llm: bool = True,
) -> list[str]

Run structural contract checks plus an LLM quality critique on a product or technical spec. Dispatches on doc["meta"]["spec_type"].

ParameterTypeDefaultDescription
docdictrequiredProduct or technical spec dict.
providerstr"openai"LLM provider for the quality critique.
modelstr | NoneNoneModel name.
llmboolTrueFalse for structural-only checks (no LLM call).

Returns: list[str] — each item prefixed [ERROR], [WARN], or [INFO]. An empty list means clean. [ERROR] items indicate structural violations that must be fixed before the spec is usable. [WARN] items are quality issues.

Examples:

from mycontext.rac import validate

issues = validate(prod)
errors = [i for i in issues if i.startswith("[ERROR]")]
if errors:
print("Spec has contract violations:", errors)

# Structural-only (no LLM call, good for CI smoke check)
issues = validate(prod, llm=False)

trace

trace(
product: dict,
technical: dict,
diff: str | None = None,
*,
provider: str = "openai",
model: str | None = None,
) -> dict

Compare a product spec to a technical spec and return a coverage/drift report. Optionally analyse a unified code diff for forbidden-tool use and out-of-scope changes.

ParameterTypeDefaultDescription
productdictrequiredProduct requirements spec dict.
technicaldictrequiredTechnical requirements spec dict.
diffstr | NoneNoneUnified diff string (e.g. from git diff). Truncated to 8 000 chars internally.
providerstr"openai"LLM provider.
modelstr | NoneNoneModel name.

Returns: dict with these keys:

KeyTypeDescription
statusstr"in_sync" | "review" | "drift_detected".
coveragedict{covered: [IDs], uncovered: [{id, kind, label}], ratio: float}.
orphanslistTechnical serves references to non-existent product IDs.
diff_impactlist[{file, touches: [IDs], forbidden_used: [tool]}].
findingslist[str]Human-readable findings, each prefixed [ERROR], [WARN], or [OK].
markdownstrFull Markdown report.

Example:

from mycontext.rac import trace, format_report
import subprocess

diff = subprocess.check_output(["git", "diff", "main...HEAD"], text=True)
report = trace(prod, tech, diff=diff)
print(format_report(report))
if report["status"] == "drift_detected":
raise SystemExit(1) # CI gate

format_report

format_report(report: dict) -> str

Render a trace() report dict as readable Markdown. Uses report["markdown"] if present (the LLM-authored version), otherwise assembles a fallback from coverage and findings.


Step 4 — Render for coding agents

project

project(
doc: dict,
to: str,
*,
provider: str = "openai",
model: str | None = None,
) -> str

Render a product or technical spec into a downstream file format. LLM-native.

ParameterTypeDefaultDescription
docdictrequiredProduct or technical spec dict.
tostrrequiredTarget format — must be one of TARGETS.
providerstr"openai"LLM provider.
modelstr | NoneNoneModel name.

Returns: str — rendered file content.

Raises: ValueError if to is not in TARGETS, or if a product-only target is given a technical spec.

TARGETS

TARGETS = ("agents-md", "claude", "cursor", "spec-kit", "kiro", "adr")
TargetOutputUse with
agents-mdAGENTS.md behavioral rulesProduct spec
claudeCLAUDE.md for Claude CodeProduct spec
cursor.cursor/rules/*.mdcProduct spec
spec-kitSpec Kit YAMLProduct spec
kiroEARS-format requirementsProduct spec
adrArchitecture Decision RecordTechnical spec

Example:

from mycontext.rac import project

agents_md = project(prod, "agents-md")
with open("AGENTS.md", "w") as f:
f.write(agents_md)

adr = project(tech, "adr")
with open("ARCHITECTURE.md", "w") as f:
f.write(adr)

Utilities

to_yaml

to_yaml(doc: dict) -> str

Serialize any spec dict to YAML (UTF-8, key order preserved, assert alias handled for safety eval blocks).

from mycontext.rac import to_yaml

print(to_yaml(prod)) # print to console
with open("product.yaml", "w") as f:
f.write(to_yaml(prod))

score_output

score_output(context_prompt: str, output: str) -> dict

Score a candidate LLM output against the context that produced it. Returns {overall: float, dimensions: dict, strengths: list, weaknesses: list}. Runs offline via OutputEvaluator.

LLMUnavailable

class LLMUnavailable(RuntimeError): ...

Raised when a provider key is required but not found in the environment. Catch this to show a user-friendly message.

from mycontext.rac import LLMUnavailable, product

try:
prod = product("...")
except LLMUnavailable as exc:
print(f"Set your API key first: {exc}")

Cognitive-pattern grounding

analyze

analyze(
text: str,
*,
kind: str = "product",
provider: str = "openai",
model: str | None = None,
) -> dict[str, dict[str, str]]

Run the curated cognitive patterns for kind ("product" or "technical") against a raw intent and return the grounding briefs that product() / technical() use internally. Call this when you want to inspect or extend the LLM analysis before spec generation.

ParameterTypeDefaultDescription
textstrrequiredRaw intent text.
kindstr"product"Which pattern set to run ("product" or "technical").
providerstr"openai"LLM provider.
modelstr | NoneNoneModel name.

Returns: dict[section, dict[pattern_name, analysis_text]]

format_brief

format_brief(notes: dict[str, dict[str, str]]) -> str

Render analyze() output as Markdown.

select_patterns

select_patterns(
intent: str,
*,
kind: str = "product",
provider: str,
model: str | None,
) -> list[str]

Ask an LLM to select the most relevant cognitive patterns for intent and kind. Used internally by product() and technical().


Structured-output contract

These are the mechanical validation layer — used internally by the generators and available to callers who want to validate specs programmatically.

parse_product / parse_technical

parse_product(data: dict) -> ProductSpec      # raises ValidationError
parse_technical(data: dict) -> TechnicalSpec # raises ValidationError

Parse a raw dict into a Pydantic model. Raises pydantic.ValidationError on any schema violation. The generators call this after every repair pass to confirm the contract is satisfied.

check_product_integrity / check_technical_integrity

check_product_integrity(data: dict) -> list[str]
check_technical_integrity(data: dict) -> list[str]

Return [ERROR] / [WARN] messages for structural violations. Does not raise — the generators use the list to drive the self-repair loop.

from mycontext.rac import check_product_integrity

issues = check_product_integrity(prod)
errors = [i for i in issues if i.startswith("[ERROR]")]
print(f"{len(errors)} contract violations")

normalize_product / normalize_technical

normalize_product(data: dict) -> dict
normalize_technical(data: dict) -> dict

Fix common LLM structural mistakes in-place and return the dict:

  • Rubric criteria placed as sibling keys → moved inside criteria list
  • Gate dicts placed as sibling keys → moved inside gates.items
  • suggested_policy → renamed to policy
  • Empty baselines / budgets / monitoring → replaced with _note stubs
  • String open_questions items → converted to dicts with auto-assigned IDs
  • Stray root-level keys that belong in meta → removed
  • meta.assumptions with empty root assumptions → rescued to root (technical)

Call these before validate() or parse_*() if you loaded a spec from a file that may not have been generated by the current version.


End-to-end Python example

import os
from mycontext.rac import (
assess, product, technical, trace, format_report,
validate, project, to_yaml, LLMUnavailable,
)

os.environ["OPENAI_API_KEY"] = "sk-..."

INTENT = (
"Sales spends too much time writing follow-up emails after demos. "
"We want to speed that up while keeping our tone on-brand. "
"Reps still review everything before it goes to a prospect."
)

try:
# Step 0 — Understand the intent
brief = assess(INTENT, model="gpt-4o")
print(brief.to_markdown())

# Step 1 — What & why
prod = product(brief, model="gpt-4o")

# Step 2 — How (traceable to product IDs)
tech = technical(product=prod, model="gpt-4o")

# Step 3 — Validate both
for spec, name in [(prod, "product"), (tech, "technical")]:
issues = validate(spec, llm=False)
errors = [i for i in issues if i.startswith("[ERROR]")]
if errors:
print(f"[{name}] contract violations:", errors)

# Step 4 — Check coverage
report = trace(prod, tech)
print(format_report(report))

# Step 5 — Render for your coding agent
agents_md = project(prod, "agents-md")
print(agents_md)

# Save YAML
with open("product.yaml", "w") as f:
f.write(to_yaml(prod))
with open("technical.yaml", "w") as f:
f.write(to_yaml(tech))

except LLMUnavailable as exc:
print(f"API key required: {exc}")