Agent Alignment Layer
Retrieval answers "what did we decide?" when somebody thinks to ask. The Agent Alignment Layer is the other half: it lets an AI coding assistant (Claude Code, Cursor, Copilot) check a proposed action against your decision graph before it takes it, whether or not anyone thought to ask.
This is the part no general-purpose retrieval can do. A contradiction is semantically opposite to the decision it violates, so the decision you are about to break is the one a similarity search is least likely to hand you unprompted.
How it works
- The agent proposes an action - open a PR, create a ticket, change a config.
- It calls
check_proposed_action(orPOST /alignment/check). - Align retrieves candidate decisions by vector similarity.
- If a candidate is close enough, the Brain runs the full conflict taxonomy over it and explains the contradiction in the decision's own terms.
- The agent receives
aligned/conflicting/no-context/unknown.
Retrieval thresholds
| Threshold | Value | What it does |
|---|---|---|
| Vector floor | 0.50 | Minimum similarity for a decision to be considered at all |
| High-similarity | 0.65 | Above this, the Brain is asked to confirm and explain |
| Organization floor | 0.30 | Org-wide standards are pulled at a lower floor on purpose - a team action that contradicts a company standard is usually worded oppositely and scores below the normal floor, but still has to be checked |
unknown is not a pass - this is the important part
If the analysis service is unavailable, slow, or degraded, the check returns unknown, never
a fall-through aligned.
{
"status": "unknown",
"reason": "brain_timeout",
"confidence": 0,
"relevant_decisions": [],
"message": "The alignment check could not be completed - the analysis service was unavailable. This is NOT a pass: treat it as unchecked and review the relevant decisions (or retry) before proceeding."
}
reason is one of brain_timeout, brain_error or brain_degraded.
The control fails closed. An earlier version of this endpoint aborted the analysis on a 5s
timeout and reported the result as a fast-check aligned, which silently discarded correct
conflicts - a check that cannot fail is not a check. If you are building on this API, treat
unknown exactly as you would treat conflicting: surface it to the human. An agent that maps
unknown onto "proceed" has re-introduced the bug.
Status meanings
| Status | Meaning | The agent should... |
|---|---|---|
aligned | Consistent with, or unrelated to, existing decisions | Proceed |
conflicting | Contradicts one or more active decisions | Stop. Surface the decisions and ask the human |
no-context | No relevant decisions found | Proceed, and consider capturing this decision afterwards |
unknown | The check could not run | Stop. Surface "could not check" - do not treat as a pass |
A second, easily-missed case: the top-level status can be aligned while a decision in
relevant_decisions has its own status of conflicted. That means a later decision overrode
the one you matched, so aligned only tells you that you agree with a stale record. Check both.
MCP tool
The hosted server registers this tool as check_proposed_action. There is a diff-shaped
sibling, check_alignment, which takes a git diff and prepends the changed file paths -
both resolve to the same endpoint.
Input
{
"action_type": "jira_ticket",
"content": "Migrate user service to MongoDB for flexible schema",
"context": "Team: Backend, Project: AUTH"
}
Response (conflicting)
{
"status": "conflicting",
"confidence": 0.91,
"relevant_decisions": [
{
"id": "d-42",
"title": "Use PostgreSQL for all services",
"summary": "Team decided Postgres is our only data store",
"status": "active",
"similarity": 0.87,
"cite": "align-stack#1234",
"decision_url": "https://app.align.tech/decisions/d-42",
"url": "https://github.com/acme/api/pull/1234"
}
],
"conflicts": [
{
"decision_id": "d-42",
"title": "Use PostgreSQL for all services",
"reason": "Proposes MongoDB which contradicts the Postgres-only decision",
"severity": "critical",
"suggested_resolution": "Review decision d-42 before proceeding"
}
],
"check_event_id": "016d5567-e6fc-4c95-9210-a22bb107bbd0",
"message": "This action conflicts with 1 existing decision(s). Review the conflicts before proceeding."
}
decision_url opens the decision in Align; url is where it was originally decided. They are
not interchangeable - do not present one as the other.
Close the loop: rate the conflict
Every check returns a check_event_id. Once you or the user have acted on a flag, record
whether it was right:
{ "tool": "rate_conflict", "check_event_id": "016d5567-…", "verdict": "correct" }
Or over REST: POST /alignment/conflicts/{eventId}/feedback. A 409 with error: "feedback_conflict" means the rating could not be verifiably recorded: nothing was written, and retrying will not succeed - surface it to a human.
This is the only signal that tells the graph whether its flags are useful. A flag nobody judges
teaches it nothing, and adjudicated precision is what GET /alignment/impact reports back.
REST API
curl -X POST https://api.align.tech/alignment/check \
-H "Authorization: Bearer <YOUR_API_KEY>" \
-H "Content-Type: application/json" \
-d '{
"action_type": "pull_request",
"content": "Add MongoDB driver to user service",
"context": "Relates to JIRA-1234"
}'
The tenant comes from your API key. Do not send x-tenant-id on an authenticated request -
it is ignored when an auth context is present, and it is only honoured for trusted
service-to-service calls. See Trust & Security.
Action types
action_type | Use for |
|---|---|
jira_ticket | Jira issue creation |
pull_request | GitHub/GitLab PR description |
slack_message | Slack or Teams messages |
commit_message | Git commit messages |
general | Anything else |
Latency - budget for seconds, not milliseconds
The Brain runs an LLM over the full conflict taxonomy, and that is the point: an earlier 5s timeout aborted it on every real check.
| Default analysis timeout | 20,000 ms (ALIGNMENT_ANALYZE_TIMEOUT_MS, digits only - "20s" is ignored and falls back to the default) |
| Shipped in the Helm chart | 60,000 ms - a ceiling, not a target |
| Measured in production, 2026-08-04 | p50 ~15s, p95 ~29s |
Two consequences worth designing for. Do not put this on a keystroke path - it belongs at a
decision point (pre-PR, pre-commit, pre-ticket), where seconds are acceptable. And set your own
client timeout above the server's, or you will manufacture the unknown you were trying to
avoid.
Agent integration example
const check = await mcp.callTool('check_proposed_action', {
action_type: 'jira_ticket',
content: `${ticket.title}\n\n${ticket.description}`,
context: `Project: ${ticket.project}`,
});
// unknown is handled FIRST and identically to conflicting - it is not a pass.
if (check.status === 'unknown') {
return `Align could not check this ticket (${check.reason}). Treat it as unchecked.`;
}
if (check.status === 'conflicting') {
return `This ticket conflicts with existing decisions:\n${
check.conflicts.map((c) => `- ${c.title}: ${c.reason}`).join('\n')
}\n\nProceed anyway?`;
}
In CI
The align CLI wraps the same endpoint for pull requests:
align check --ci --base origin/main --block-on-critical
Setup
Connect your assistant to the Align MCP server - see the AI Assistants (MCP) guide. For REST, authenticate with your API key.