Complete reference for the PCL (Persona Control Language) syntax and semantics.
Version: 1.0.0 Last Updated: 2026-01-16
- Lexical Grammar
- Syntactic Grammar
- Type System
- Declarations
- Expressions
- Lifecycle Hooks
- Keywords
- Operators
- Comments
Reserved words in PCL:
persona team workflow skill
extends if else while
for in return import
export from as true
false null let const
function type interface enum
Identifier ::= [a-zA-Z_][a-zA-Z0-9_]*Examples:
Assistant
SecurityExpert
my_persona
persona123
_internal
Restrictions:
- Cannot start with a digit
- Cannot be a keyword
- Case-sensitive
StringLiteral ::= '"' StringChar* '"'Examples:
"Hello, world!"
"Multi-line strings\nare supported"
"Escape sequences: \" \\ \n \t"
Escape sequences:
\"- Double quote\\- Backslash\n- Newline\t- Tab\r- Carriage return
IntLiteral ::= [0-9]+
FloatLiteral ::= [0-9]+ '.' [0-9]+Examples:
42 // Integer
3.14 // Float
0 // Integer
100.0 // Float
true
false
null
+ - * / % // Arithmetic
== != < > <= >= // Comparison
&& || // Logical
! // Unary not
= // Assignment
matches // String matching
in // Membership
{ } // Blocks
( ) // Grouping, parameters
[ ] // Arrays (future)
, // Separator
; // Statement terminator (optional)
: // Type annotation
-> // Workflow arrow
// Single-line comment
/*
* Multi-line comment
*/
Program ::= Statement*
Statement ::=
| PersonaDeclaration
| TeamDeclaration
| WorkflowDeclaration
| TypeDeclaration
| FunctionDeclaration
| VariableDeclarationPersonaDeclaration ::=
'persona' Identifier Extends? '{' PersonaMember* '}'
Extends ::= 'extends' TypeReference (',' TypeReference)*
PersonaMember ::=
| Assignment
| SkillBlock
| ConstraintBlock
| MethodDeclarationBasic persona:
persona Assistant {
intent = "Help users with their tasks"
}
With inheritance:
persona SecurityExpert extends Expert {
intent = "Provide security analysis"
}
Full example:
persona SecurityExpert extends Expert {
// Assignments
intent = "Security analysis and threat modeling"
maxTokens = 4096
temperature = 0.7
// Skills block
skills {
"Threat modeling"
"Vulnerability assessment"
"Code review"
}
// Constraints block
constraints {
"Always assume breach"
"Apply defense in depth"
maxResponseTime <= 5
temperature >= 0.0
}
}
SkillBlock ::= 'skills' '{' StringLiteral* '}'Example:
skills {
"Threat modeling"
"Vulnerability assessment"
"Code review"
"Security best practices"
}
ConstraintBlock ::= 'constraints' '{' Constraint* '}'
Constraint ::=
| StringConstraint
| ExprConstraint
StringConstraint ::= StringLiteral
ExprConstraint ::= Identifier ComparisonOp ExpressionExamples:
constraints {
// String constraints
"Always assume breach"
"Apply least privilege"
// Expression constraints
maxTokens <= 8000
maxTokens >= 1000
temperature >= 0.0
temperature <= 1.0
}
TeamDeclaration ::=
'team' Identifier '{' TeamMember* '}'
TeamMember ::=
| 'members' '{' Identifier* '}'
| 'primary' Identifier
| 'quorum' IntLiteral
| 'merge' IdentifierExample:
team SecurityReview {
members {
SecurityExpert
Auditor
Architect
}
primary SecurityExpert
quorum 2
merge Consensus
}
WorkflowDeclaration ::=
'workflow' Identifier '{' WorkflowExpression* '}'
WorkflowExpression ::=
| SequenceExpr
| ParallelExpr
| ChoiceExpr
| ConditionalExpr
| LoopExpr
| MergeExprExamples:
workflow CodeReview {
// Sequential
Developer -> Reviewer -> Approver
// Parallel
(SecurityReview || CodeReview || PerformanceReview)
// With merge
(Reviewer1 || Reviewer2) -> merge(Consensus)
}
TypeDeclaration ::=
'type' Identifier TypeParameters? '=' TypeExamples:
type UserId = String
type Result = Success | Error
type Container<T> = { value: T }
VariableDeclaration ::=
('let' | 'const') Identifier (':' Type)? '=' ExpressionExamples:
let count = 10
const maxRetries = 3
let message: String = "Hello"
String // Text
Int // Integer numbers
Float // Floating-point numbers
Bool // true or false
Void // No value
Never // Unreachable
ArrayType ::= 'Array' '<' Type '>'Examples:
Array<String>
Array<Int>
Array<PersonaType>
MapType ::= 'Map' '<' Type ',' Type '>'Examples:
Map<String, Int>
Map<String, PersonaType>
UnionType ::= Type ('|' Type)+Examples:
String | Int
Success | Error | Pending
FunctionType ::= '(' ParameterList? ')' '=>' Type
ParameterList ::= Parameter (',' Parameter)*
Parameter ::= Identifier ':' TypeExamples:
(input: String) => String
(x: Int, y: Int) => Float
() => Void
Variables can have explicit type annotations:
let name: String = "Alice"
let count: Int = 42
let score: Float = 98.5
let isEnabled: Bool = true
persona PersonaName [extends ParentName] {
// Property assignments
property1 = value1
property2: Type = value2
// Skills block
skills {
"Skill 1"
"Skill 2"
}
// Constraints block
constraints {
"String constraint"
field <= value
field >= value
}
// Methods (limited parser support)
method methodName(param: Type): ReturnType {
// Method body
}
}
Required elements:
- Persona name
Optional elements:
- Extends clause (inheritance)
- Property assignments
- Skills block
- Constraints block
- Methods
// Simple assignment
intent = "Description"
// With type annotation
maxTokens: Int = 4096
// Expression
temperature = 0.5 + 0.2
// Identifiers
userName
maxRetries
// Literals
"Hello"
42
3.14
true
false
null
// Parenthesized
(expression)
a + b // Addition
a - b // Subtraction
a * b // Multiplication
a / b // Division
a % b // Modulo
a == b // Equal
a != b // Not equal
a < b // Less than
a > b // Greater than
a <= b // Less than or equal
a >= b // Greater than or equal
a && b // Logical AND
a || b // Logical OR
str matches pattern // String pattern matching
item in array // Membership test
!condition // Logical NOT
-value // Negation
From highest to lowest:
- Primary expressions (identifiers, literals, parentheses)
- Unary operators (
!,-) - Multiplicative (
*,/,%) - Additive (
+,-) - Comparison (
<,>,<=,>=) - Equality (
==,!=) - Logical AND (
&&) - Logical OR (
||) - Assignment (
=)
Example:
a + b * c // Evaluated as: a + (b * c)
a && b || c // Evaluated as: (a && b) || c
!a && b // Evaluated as: (!a) && b
The following identifiers are reserved and cannot be used as variable or type names:
// Declaration keywords
persona
team
workflow
skill
type
interface
enum
// Control flow
if
else
while
for
in
return
break
continue
// Modifiers
extends
implements
pub
private
// Values
true
false
null
// Variable declarations
let
const
var
// Import/Export
import
export
from
as
// Functions
function
fn
// Future reserved
async
await
match
case
when
| Operator | Description | Example |
|---|---|---|
== |
Equal | a == b |
!= |
Not equal | a != b |
< |
Less than | a < b |
> |
Greater than | a > b |
<= |
Less or equal | a <= b |
>= |
Greater or equal | a >= b |
matches |
Pattern match | str matches "\\d+" |
in |
Membership | item in array |
| Operator | Description | Example |
|---|---|---|
+ |
Addition | a + b |
- |
Subtraction | a - b |
* |
Multiplication | a * b |
/ |
Division | a / b |
% |
Modulo | a % b |
| Operator | Description | Example |
|---|---|---|
&& |
Logical AND | a && b |
|| |
Logical OR | a || b |
! |
Logical NOT | !a |
| Operator | Description | Example |
|---|---|---|
= |
Assignment | x = 10 |
// This is a single-line comment
persona Test { } // Comment after code
/*
* This is a multi-line comment
* spanning multiple lines
*/
persona Test {
/*
* Skills for this persona
*/
skills {
"Skill 1"
}
}
// Security expert persona with full configuration
persona SecurityExpert extends Expert {
// Basic properties
intent = "Provide comprehensive security analysis"
id = "SEC-001"
version = "1.0.0"
// Numeric configuration
maxTokens: Int = 4096
temperature: Float = 0.7
topP: Float = 0.9
// Skills
skills {
"Threat modeling (STRIDE, DREAD)"
"Vulnerability assessment (OWASP Top 10)"
"Security code review"
"Penetration testing"
"Compliance (SOC2, ISO 27001)"
}
// Constraints
constraints {
// Behavioral guidelines
"Always assume breach"
"Apply defense in depth"
"Use principle of least privilege"
"Provide actionable recommendations"
// Numeric constraints
maxTokens <= 8000
maxTokens >= 1000
temperature >= 0.0
temperature <= 1.0
topP >= 0.0
topP <= 1.0
}
}
// Base expert persona
persona Expert {
skills {
"Research"
"Analysis"
"Documentation"
}
constraints {
"Provide evidence-based recommendations"
"Cite sources"
}
}
// Specialized security expert
persona SecurityExpert extends Expert {
intent = "Security analysis"
skills {
"Threat modeling"
"Vulnerability assessment"
}
constraints {
"Always assume breach"
}
}
// Specialized data expert
persona DataExpert extends Expert {
intent = "Data analysis and insights"
skills {
"Statistical analysis"
"Data visualization"
}
constraints {
"Always cite data sources"
}
}
persona RateLimitedAssistant {
// Configuration
maxRequests: Int = 100
requestWindow: Int = 60 // seconds
maxConcurrent: Int = 5
constraints {
// Rate limiting
maxRequests >= 10
maxRequests <= 1000
requestWindow >= 1
requestWindow <= 3600
// Concurrency
maxConcurrent >= 1
maxConcurrent <= 10
}
}
(* Program structure *)
Program ::= Statement*
Statement ::= PersonaDeclaration
| TeamDeclaration
| WorkflowDeclaration
| TypeDeclaration
| FunctionDeclaration
| VariableDeclaration
(* Persona *)
PersonaDeclaration ::= 'persona' Identifier Extends? '{' PersonaMember* '}'
Extends ::= 'extends' TypeReference (',' TypeReference)*
PersonaMember ::= Assignment
| SkillBlock
| ConstraintBlock
| MethodDeclaration
(* Skills *)
SkillBlock ::= 'skills' '{' StringLiteral* '}'
(* Constraints *)
ConstraintBlock ::= 'constraints' '{' Constraint* '}'
Constraint ::= StringConstraint | ExprConstraint
StringConstraint ::= StringLiteral
ExprConstraint ::= Identifier ComparisonOp Expression
(* Expressions *)
Expression ::= LogicalOrExpr
LogicalOrExpr ::= LogicalAndExpr ('||' LogicalAndExpr)*
LogicalAndExpr ::= EqualityExpr ('&&' EqualityExpr)*
EqualityExpr ::= ComparisonExpr (('==' | '!=') ComparisonExpr)*
ComparisonExpr ::= AdditiveExpr (('<' | '>' | '<=' | '>=') AdditiveExpr)*
AdditiveExpr ::= MultiplicativeExpr (('+' | '-') MultiplicativeExpr)*
MultiplicativeExpr ::= UnaryExpr (('*' | '/' | '%') UnaryExpr)*
UnaryExpr ::= ('!' | '-') UnaryExpr | PrimaryExpr
PrimaryExpr ::= Identifier | Literal | '(' Expression ')'
(* Literals *)
Literal ::= StringLiteral | IntLiteral | FloatLiteral
| BooleanLiteral | NullLiteral
StringLiteral ::= '"' StringChar* '"'
IntLiteral ::= [0-9]+
FloatLiteral ::= [0-9]+ '.' [0-9]+
BooleanLiteral ::= 'true' | 'false'
NullLiteral ::= 'null'
(* Identifiers *)
Identifier ::= [a-zA-Z_][a-zA-Z0-9_]*
(* Types *)
Type ::= PrimitiveType | ArrayType | UnionType | Identifier
PrimitiveType ::= 'String' | 'Int' | 'Float' | 'Bool' | 'Void'
ArrayType ::= 'Array' '<' Type '>'
UnionType ::= Type ('|' Type)+
(* Operators *)
ComparisonOp ::= '==' | '!=' | '<' | '>' | '<=' | '>=' | 'matches' | 'in'Lifecycle hooks let you execute code at specific points in a persona's, team's, or workflow's lifecycle. Hooks are defined using the @hookName decorator syntax.
PCL provides 12 built-in lifecycle hooks:
| Hook | Trigger | Use Case |
|---|---|---|
@onActivate |
Persona/team becomes active | Initialize resources, log activation |
@onDeactivate |
Persona/team becomes inactive | Cleanup, save state |
@onError |
Error occurs during execution | Error handling, logging, alerts |
@onMessage |
New message received | Message preprocessing, validation |
@onStep |
Workflow advances to next step | Progress tracking, logging |
@onComplete |
Execution completes successfully | Finalization, notifications |
@beforeMerge |
Before merging multiple personas | Prepare state for merging |
@afterMerge |
After merging multiple personas | Validate merged result |
@onSpawn |
New persona instance spawned | Initialize child persona |
@onDespawn |
Persona instance terminated | Cleanup child resources |
@onTimeout |
Operation times out | Handle timeout gracefully |
@onRetry |
Retry attempt begins | Log retry, adjust parameters |
HookDeclaration ::= '@' HookName [ '(' Parameters ')' ] Block
HookName ::=
| 'onActivate' | 'onDeactivate'
| 'onError' | 'onMessage'
| 'onStep' | 'onComplete'
| 'beforeMerge' | 'afterMerge'
| 'onSpawn' | 'onDespawn'
| 'onTimeout' | 'onRetry'
Parameters ::= Parameter (',' Parameter)*
Parameter ::= Identifier [ ':' Type ]Persona hooks:
persona SEC {
intent: "Security analysis"
@onActivate {
log.info("Security analyst activated")
metrics.increment("security.activations")
}
@onError(error) {
log.error(`Security analysis failed: ${error.message}`)
notify("security-team", error)
}
@onComplete(result) {
log.info(`Analysis complete. Risk score: ${result.riskScore}`)
}
}
Team hooks:
team SecurityReview {
members: [SEC, AUDIT, ARCHI]
@onActivate {
// Initialize shared resources
createSharedWorkspace()
}
@beforeMerge(responses) {
// Validate all responses before merging
validateResponses(responses)
}
@onComplete(result) {
if result.riskScore > 7 {
alert("high-risk", result)
}
}
}
Workflow hooks:
workflow CodeReview {
steps: DEV -> (ARCHI || SEC) -> CRITIC
@onStep(step) {
log.debug(`Processing step: ${step.name}`)
metrics.recordStep(step.name, step.duration)
}
@onTimeout {
log.warn("Code review timed out")
notifyReviewers("timeout")
}
@onError(error) {
log.error(`Workflow failed: ${error}`)
rollbackChanges()
}
@onComplete(result) {
publishReviewResults(result)
notifyAuthor(result)
}
}
Hooks can access context through parameters:
Error context:
@onError(error) {
log.error(error.message)
log.error(error.stack)
log.error(error.code)
}
Message context:
@onMessage(message) {
if message.priority == "urgent" {
escalate(message)
}
}
Step context:
@onStep(step) {
console.log(step.name) // Step identifier
console.log(step.status) // "running", "complete", "failed"
console.log(step.duration) // Execution time
console.log(step.result) // Step output
}
Result context:
@onComplete(result) {
console.log(result.status) // "success", "failed"
console.log(result.output) // Final output
console.log(result.metadata) // Additional data
}
Hooks execute in a predictable order:
Persona lifecycle:
1. @onActivate ← Persona starts
2. @onMessage ← Each message received
3. @onError ← If error occurs (optional)
4. @onComplete ← Execution finishes
5. @onDeactivate ← Persona stops
Team lifecycle:
1. @onActivate ← Team starts
2. @beforeMerge ← Before combining responses
3. @afterMerge ← After combining responses
4. @onComplete ← Team finishes
5. @onDeactivate ← Team stops
Workflow lifecycle:
1. @onActivate ← Workflow starts
2. @onStep ← Each step execution
3. @onTimeout ← If timeout occurs (optional)
4. @onRetry ← If retry needed (optional)
5. @onError ← If error occurs (optional)
6. @onComplete ← Workflow finishes
7. @onDeactivate ← Workflow stops
Nested execution:
Team @onActivate
├─> Persona A @onActivate
├─> Persona B @onActivate
├─> Persona A @onMessage
├─> Persona B @onMessage
├─> Team @beforeMerge
├─> Team @afterMerge
├─> Persona A @onDeactivate
├─> Persona B @onDeactivate
└─> Team @onDeactivate
1. Keep hooks focused and fast
// ✅ GOOD: Quick operation
@onActivate {
log.info("Activated")
metrics.increment("activations")
}
// ❌ BAD: Slow operation blocks activation
@onActivate {
loadLargeDatabase() // Too slow!
trainModel() // Too slow!
}
2. Handle errors in hooks
// ✅ GOOD: Graceful error handling
@onComplete(result) {
try {
saveToDatabase(result)
} catch (error) {
log.error("Failed to save:", error)
// Don't fail the entire operation
}
}
// ❌ BAD: Unhandled errors
@onComplete(result) {
saveToDatabase(result) // Throws on failure!
}
3. Use appropriate hooks
// ✅ GOOD: Right hook for the job
@onActivate {
initializeResources()
}
@onDeactivate {
cleanupResources()
}
// ❌ BAD: Wrong hook
@onComplete {
initializeResources() // Too late!
}
4. Don't modify state unexpectedly
// ✅ GOOD: Read-only observation
@onStep(step) {
log.info(step.name)
metrics.record(step)
}
// ❌ BAD: Modifying workflow state
@onStep(step) {
step.status = "skipped" // Don't do this!
}
5. Use hooks for cross-cutting concerns
persona DataAnalyst {
// Logging
@onActivate { log.info("Started") }
@onComplete { log.info("Finished") }
// Metrics
@onStep(step) { metrics.record(step) }
// Error tracking
@onError(error) { errorTracker.report(error) }
// Business logic stays in methods
pub fn analyze(data: Data) -> Report {
// Core logic here
}
}
Rate limiting:
persona APIConsumer {
@onMessage(message) {
if rateLimiter.isExceeded() {
throw RateLimitError("Too many requests")
}
rateLimiter.increment()
}
@onComplete {
rateLimiter.reset()
}
}
Distributed tracing:
workflow DataPipeline {
@onActivate {
span = tracer.startSpan("data-pipeline")
}
@onStep(step) {
span.addEvent(step.name)
}
@onError(error) {
span.recordError(error)
}
@onComplete(result) {
span.setStatus("success")
span.end()
}
}
Resource pooling:
team DatabaseTeam {
@onActivate {
pool = createConnectionPool({
min: 2,
max: 10,
timeout: 30s
})
}
@beforeMerge {
// Ensure all connections are returned
pool.waitForConnections()
}
@onDeactivate {
pool.drain()
pool.close()
}
}
Circuit breaker:
persona ExternalService {
@onError(error) {
circuitBreaker.recordFailure()
if circuitBreaker.isOpen() {
throw CircuitOpenError("Service unavailable")
}
}
@onComplete {
circuitBreaker.recordSuccess()
}
@onTimeout {
circuitBreaker.recordTimeout()
}
}
Audit logging:
persona SecureOperator {
@onActivate {
audit.log({
event: "activation",
user: currentUser,
timestamp: now()
})
}
@onMessage(message) {
audit.log({
event: "message",
content: sanitize(message),
timestamp: now()
})
}
@onComplete(result) {
audit.log({
event: "completion",
result: sanitize(result),
timestamp: now()
})
}
}
Hooks can be tested by triggering lifecycle events:
import { test, expect } from 'vitest';
import { createPersona } from '@pcl/runtime';
test('onActivate hook executes', async () => {
let activated = false;
const persona = createPersona({
name: 'TEST',
hooks: {
onActivate: () => {
activated = true;
},
},
});
await persona.activate();
expect(activated).toBe(true);
});
test('onError hook handles failures', async () => {
let errorCaught = null;
const persona = createPersona({
name: 'TEST',
hooks: {
onError: (error) => {
errorCaught = error;
},
},
});
await persona.execute(() => {
throw new Error('Test error');
});
expect(errorCaught).toBeTruthy();
expect(errorCaught.message).toBe('Test error');
});Last Updated: 2026-01-17 Version: 1.0.0 Status: Production-ready with documented limitations