Agent injection
tickety-ai reads .tickety/devcontract.json at every session start and injects it as the first block of Claude's system prompt — before any steering .md files. This is Phase 1 of the DevContract model: contract acknowledgement before any work begins.
How it works
When you open a session in tickety-ai, the extension calls loadSteeringFiles(cwd). This function:
- Looks for
.tickety/devcontract.json. - If found, parses and passes it to
renderContract(), which formats it as a compact, structured text block. - The contract block is placed first —
parts.unshift(contractBlock)— so it is always the first content in the system prompt. - Steering
.mdfiles from.tickety/steering/follow. - If no contract file exists, the session continues with steering files only. No error is raised.
The rendered contract block
renderContract() converts the JSON object into a compact, plain-text format optimised for LLM consumption. An example for the ticketyboo.dev free-tier contract:
== ACTIVE DEVCONTRACT (v1.0) ==
Project: ticketyboo.dev | Client: fenderfonic | Contractor: claude-code
STACK: python 3.12 | Runtime: aws_lambda | Region: eu-north-1
FORBIDDEN SERVICES: ec2, fargate, lightsail, rds
ARCHITECTURE:
Required patterns: single_table_dynamodb, handler_delegates_to_domain
Forbidden patterns: fat_handler, orm_layer
COST ENVELOPE: aws_free_tier
Forbidden resources: nat_gateway, rds, secrets_manager, kms_cmk, elastic_ip
Approved exceptions: route53_hosted_zone
SECURITY:
Auth: cognito_jwt
Secrets: ssm_parameter_store ONLY
Never store secrets in: env_vars, source_code, config_files
PII handling: redact_before_llm
OWASP compliance: REQUIRED
Production deploy: REQUIRES HUMAN APPROVAL
QUALITY:
Type hints: REQUIRED on all functions
Docstrings: public_only functions
Coverage minimum: 80%
Logging: module_logger_only — no print()
Forbidden: print_statements, bare_except, mutable_defaults
COMPLIANCE: GDPR
Data deletion: hard_delete_only — never soft-delete
PII: must be classified before any LLM processing
Data residency: eu_only
AUDIT: evidence_required | signed_receipt | pr_comment_required
DEFINITION OF DONE:
✓ tests_pass | ✓ coverage_met | ✓ no_contract_violations | ✓ evidence_generated | ✓ security_scan_pass | ✓ pr_comment_posted
Scan gates: secret:critical (blocking), sast:high (blocking), dependency:critical (blocking)
== END CONTRACT — you are operating under these terms ==
What Claude does with it
Because the contract block is the first content in the system prompt, Claude reads it before any task description, before any steering files, and before any user message. The expected behaviour is:
- Phase 1 — Acknowledgement: Claude confirms it has read the contract terms before beginning any work.
- Phase 2 — Active consultation: Before any material decision (choosing a cloud service, selecting a pattern, handling a secret), Claude checks the relevant clause and states which clause applies to the decision.
- Phase 3 — Delivery confirmation: When work is complete, Claude confirms which
definition_of_donecriteria have been met and which remain outstanding.
Implementation reference
The injection is implemented in two files in the tickety-ai extension:
| File | Responsibility |
|---|---|
src/core/steering/renderContract.ts |
Converts a parsed devcontract.json object to a formatted steering block string. Handles all 8 clause families. Gracefully omits absent optional clauses. |
src/core/steering/inject.ts |
loadSteeringFiles(cwd) — reads the contract, calls renderContract(), prepends to parts[], then appends steering .md files. |
System prompt order
The full system prompt assembly order at session start:
- DevContract block — rendered from
.tickety/devcontract.json(if present) - Steering files — all
.mdfiles from.tickety/steering/, sorted alphabetically - Mode system prompt — the role definition for the current mode (Code, Architect, Debug, etc.)
- Tool definitions
- Conversation history
The contract is always first. This ensures it is never buried below mode instructions or previous conversation context.
Failure behaviour
If devcontract.json cannot be read (file missing, permission denied) or cannot be parsed (malformed JSON), loadSteeringFiles() silently continues without the contract block. No error is surfaced to the user.
To validate your contract before a session, use load_contract() from contract_schema.py in a pre-session check or CI step.