Skip to main content

Technical Requirements — technical()

Where product() answers what a system must do, technical() answers how it is built: architecture, guardrails, tool permissions, cost controls, deployment, observability, security, and failure behavior — plus an optional frontier layer (fine-tune / RL / computer-use).

Its defining feature is traceability: every control carries a serves: list of the product requirement IDs it implements, so trace() can prove coverage and catch drift.

from mycontext.rac import product, technical, to_yaml

prod = product("Aurora drafts replies to billing emails; refunds need approval.")
tech = technical(product=prod)
print(to_yaml(tech))

Signature & parameters

def technical(
text: str | None = None,
*,
product: dict | None = None,
frontier: bool = False,
execute: bool = False,
provider: str = "openai",
model: str | None = None,
) -> dict
ParameterTypeDefaultMeaning
textstr | NoneNoneNatural-language intent — the bootstrap path. Use when you don't have a product spec yet.
productdict | NoneNoneA product spec (from product()) — the rich path. Enables full serves: traceability.
frontierboolFalseInclude the advanced frontier layer (fine-tune, RL, computer-use, multimodal).
executeboolFalseUse your LLM key to answer the open questions and return a filled spec. See grounding.
providerstr"openai"Provider for execute=True.
modelstr | NoneNoneModel for execute=True; None → provider-aware default.

You must pass either product= or text. Passing neither raises ValueError.

Returns a dict with meta.spec_type == "technical_requirements".

Two entry paths

tech = technical(product=prod, frontier=True)

When you pass a product spec, technical() reads its tasks, actions, safety entries, rubrics, and gates, and wires real serves: links into every control. This is what makes trace() meaningful.

Bootstrap path — technical("<intent>")

tech = technical("A RAG assistant answers questions from our wiki; never invent answers.")

With only text, technical() still emits the full structure, but serves: links that would point at product IDs are left as TODO(OQ-n) with a nudge to generate a product spec first. meta.source will be natural_language instead of product_requirements.

The output, section by section

meta

meta:
system_name: aurora
spec_type: technical_requirements
for_product: aurora
kind: agent
spec_version: 0.1.0
status: draft
generated_by: mycontext-ai 0.13.0 technical-architect
source: product_requirements # or 'natural_language'
note: "Technical requirements authored by mycontext ... build/enforcement belongs to your stack."
review_checklist:
- Pin concrete model IDs, infra, and thresholds (replace TODO markers)
- Confirm every `serves` reference resolves to a real product requirement
- Run `mycontext rac trace` to verify product/technical coverage

architecture

architecture:
pattern: "tool_augmented_agent (plan -> act -> observe loop)" # chosen by kind
orchestration:
loop: "plan -> act -> observe"
max_steps: TODO(OQ-n) # suggested default 6
state:
short_term: "per-request scratchpad (conversation memory)"
long_term: "vector store over the trusted source" # for rag; else 'none'
model_routing_ref: cost.routing
serves: [T1] # the product tasks this architecture serves

The pattern is selected from kind: tool_augmented_agent (agent), supervisor_orchestrator (multi_agent), retrieve_then_generate (rag), or stateless_request_handler (service).

guardrails

Three layers — input, processing, output — each a list of controls with a serves: link:

guardrails:
input:
- {control: "Treat all user/tool content as data, never instructions (prompt-injection defense)", serves: [P4]}
- {control: "Schema + length validation on inbound payloads", serves: [T1]}
processing:
- {control: "Tool-call allowlist; calls outside the registry are rejected", serves: [A-X, A-3]}
- {control: "Per-request step + token budget enforced", serves: [cost]}
output:
- {control: "Groundedness check — every claim traceable to source before send", serves: [R-T1]}
- {control: "No claim that an approval-gated action completed before approval", serves: [A-3]}
- {control: "PII / cross-customer data redaction on output", serves: [P2]} # if leak safety exists

tools

The tool registry (with least-privilege scopes), forbidden tools, and the non-human-identity (service account) principle:

tools:
registry:
- {name: draft_reply, access: write, requires_approval: false, scope: TODO(OQ-n), serves: [A-2]}
- {name: send_reply, access: write, requires_approval: true, scope: TODO(OQ-n), serves: [A-3]}
forbidden:
- {tools: [delete_record, modify_history, merge_records], policy: forbidden, serves: [A-X]}
non_human_identity:
principle: "least privilege per tool; short-lived credentials"
scopes: TODO(OQ-n)

cost

cost:
budget_per_task_usd: TODO(OQ-n) # suggested default 0.40
routing:
draft_model: TODO(OQ-n)
escalation_model: TODO(OQ-n)
rule: "strong model for high/critical-risk tasks and on low confidence; cheap model otherwise"
serves: [T1]
daily_budget_usd: TODO(OQ-n) # suggested default 200
on_exhaustion: "degrade to draft-only / escalate to human; never silently drop work"

deployment, observability, security, failure_behavior

deployment:
rollout: {strategy: "shadow -> canary -> full", stages: ["5% shadow", "25% canary", "100%"], gated_by: [G-1, G-2, G-3]}
rollback: "auto-rollback if any hard gate regresses in canary"
canary_dimensions: [task risk tier, new tool usage, cost per task]

observability:
metrics:
- {name: rubric_pass_rate, serves: [G-1, G-2, G-3]}
- {name: hitl_approval_and_rejection_rate, serves: [A-3]}
- {name: cost_per_task, serves: [cost]}
- {name: "unmapped_task_rate (charter drift)", serves: [T1]}
tracing: "span per plan/act/observe step, each tool call, and each approval decision"
audit_log: {what: "all write + approval actions, immutable", serves: [A-3]}

security:
owasp_agentic_top10:
- {risk: Excessive Agency, mitigation: "forbidden-tool list + approval gates", serves: [A-X, A-3]}
- {risk: Prompt Injection, mitigation: "input-as-data guard; risky actions stay HITL", serves: [P4]}
- {risk: Sensitive Information Disclosure, mitigation: "output PII/cross-customer redaction", serves: [P2]}
secrets: "no secrets in prompts/logs; pull from a secrets manager at call time"
data_handling: "PII minimized + redacted in traces" # when pii detected

failure_behavior:
on_tool_error: {strategy: "retry with backoff then escalate", max_retries: TODO(OQ-n)}
on_low_confidence: {strategy: "route to human; never guess", serves: [T_oos, P1, P4]}
degraded_mode: "draft-only / read-only when a dependency is down; surface status, don't fail silently"

frontier (optional, frontier=True)

frontier:
model_layer_advanced: {fine_tune: TODO(OQ-n), distillation: "...", self_host: "..."}
agent_rl: {enabled: false, note: TODO(OQ-n)}
frontier_capabilities: {computer_use: false, multimodal: TODO(OQ-n), voice: false, agent_to_agent: false}
serves: [T1]

The serves traceability model

Every risk-bearing control links to the product requirement ID(s) it implements:

Product ID prefixWhat it is
T*A task (e.g. T1, T_oos)
A*An action (e.g. A-3, A-X)
P*A safety requirement (e.g. P1, P4)
G*A release gate (e.g. G-1)
R-*A rubric (e.g. R-T1)
costThe cost section (a non-ID anchor)

trace() uses these links to compute coverage. See Trace & validation.

Validate a technical spec

from mycontext.rac import technical, validate

issues = validate(technical(product=prod))
# Technical validator checks required sections, guardrail layers, forbidden tools,
# and open-question bookkeeping. It never emits the product-only "Uncovered" errors.

Next