update first - 84

This commit is contained in:
thanhnv
2026-06-30 02:21:39 +09:00
commit 07ac1bdcdd
561 changed files with 88164 additions and 0 deletions
@@ -0,0 +1,4 @@
{"timestamp":"2026-06-29T17:01:08Z","trace_id":"98e40b76-6db7-4357-83c4-b879308cd645","severity":"WARN","resource":{"service.name":"demo.agent","service.version":"1.0.0"},"body":{"message":"Alert triggered: hallucination-suspected","alert.type":"hallucination-suspected","step.name":"step-1-srs"},"attributes":{"latency_ms":220,"status":"success"}}
{"timestamp":"2026-06-29T17:01:09Z","trace_id":"45331383-aaed-47b7-bfa1-58eba82adcd3","severity":"WARN","resource":{"service.name":"demo.agent","service.version":"1.0.0"},"body":{"message":"Alert triggered: execution-failed","alert.type":"execution-failed","step.name":"failing-step"},"attributes":{"latency_ms":198,"status":"failed"}}
{"timestamp":"2026-06-29T17:01:22Z","trace_id":"997d7a8d-7d82-4b85-acd5-7b95ceaa0aaf","severity":"WARN","resource":{"service.name":"unknown-agent","service.version":"1.0.0"},"body":{"message":"Alert triggered: execution-failed","alert.type":"execution-failed","step.name":"unknown-step"},"attributes":{"latency_ms":210,"status":"failed"}}
{"timestamp":"2026-06-29T17:01:23Z","trace_id":"d5857d18-83ac-4dbf-8d47-aebb4549255d","severity":"WARN","resource":{"service.name":"adv","service.version":"1.0.0"},"body":{"message":"Alert triggered: hallucination-suspected","alert.type":"hallucination-suspected","step.name":"step-1-srs"},"attributes":{"latency_ms":205,"status":"success"}}
@@ -0,0 +1,86 @@
version: 1.0
description: AgentOps Alert Rules
alerts:
- id: ALERT-001
name: High Latency
condition: latency_ms > 5000
severity: medium
action:
- log
- notify
- id: ALERT-002
name: High Retry
condition: retry_count > 2
severity: medium
action:
- log
- notify
- id: ALERT-003
name: Failure Detected
condition: status == "failed"
severity: high
action:
- log
- notify
- escalate
- id: ALERT-004
name: High Cost Spike
condition: cost_estimate > 0.05
severity: medium
action:
- log
- id: ALERT-005
name: Hallucination Risk
condition: output contains ("maybe" or "might be incorrect")
severity: low
action:
- log
- flag
- id: ALERT-006
name: Token Overuse
condition: total_tokens > 5000
severity: medium
action:
- log
- notify
notification:
channels:
- type: log
path: ".specify/agentops/alerts.log"
- type: console
enabled: true
# future integration
# - type: slack
# webhook: "https://hooks.slack.com/..."
escalation:
enabled: true
rules:
- severity: high
action: immediate_alert
- severity: medium
action: batched_alert
- severity: low
action: log_only
level4_gate:
alert_log_must_exist: true
failed_step_must_emit_alert: true
retry_threshold_must_emit_alert: true
cost_spike_must_emit_alert: true
@@ -0,0 +1,76 @@
version: 1.0
description: CASAN H6 per-step hallucination signal tracking
tracking:
enabled: true
method: cross_reference_check
log_path: .specify/logs/cost/metrics.jsonl
field_name: hallucination_signals
steps_with_tracking:
- step: step-1-srs
reference_docs:
- docs/input/okr-requirement.md
signals:
- ungrounded_feature_claim
- invented_requirement
- undefined_acronym
keywords_indicating_hallucination:
- "I assume"
- "typically"
- "usually includes"
- "standard practice suggests"
- step: step-3-spec
reference_docs:
- docs/output/ipa-docs/srs/
- docs/output/ipa-docs/bd/
signals:
- invented_api_endpoint
- schema_mismatch
- undefined_term
- ungrounded_business_rule
keywords_indicating_hallucination:
- "assumed from context"
- "common pattern would be"
- "likely the intent is"
- step: step-6-plan
reference_docs:
- docs/output/specs/
signals:
- invented_dependency
- wrong_module_assignment
- unspecified_library
keywords_indicating_hallucination:
- "might need"
- "could potentially use"
- "I believe"
- step: step-10-implement
reference_docs:
- docs/output/specs/
signals:
- undocumented_endpoint
- wrong_field_type
- missing_required_field
- hardcoded_value_not_in_spec
keywords_indicating_hallucination:
- "// TODO: confirm"
- "// assumed"
- "// placeholder"
- step: step-11-review-code
reference_docs:
- docs/output/specs/
- backend/
- frontend/
signals:
- false_security_clearance
- invented_test_result
- unverified_compliance_claim
thresholds:
warn: 3
block: 7
note: "warn=log warning; block=force retry of step"
@@ -0,0 +1,52 @@
version: 1.0
description: CASAN H6 AgentOps metrics and scoring configuration
collection:
enabled: true
trace_format: json
metrics_format: jsonl
trace_dir: ".specify/logs/trace"
metrics_log: ".specify/logs/cost/metrics.jsonl"
alert_log: ".specify/agentops/alerts.log"
dimensions:
- trace_id
- timestamp
- agent
- step
- status
- exit_code
- latency_ms
- retry_count
- input_tokens
- output_tokens
- total_tokens
- cost_estimate
- alerts
- input_hash
- output_hash
cost_model:
default_cost_per_1k_tokens_usd: 0.002
override_env: CASAN_COST_PER_1K
thresholds:
latency_alert_ms: 5000
retry_alert_threshold: 2
token_alert_threshold: 5000
failure_alert: true
level4_gate:
required: true
minimum_evidence:
traces_per_pipeline_step: 1
metrics_log_present: true
alert_rules_present: true
failure_status_recorded: true
level5_readiness:
required_additions:
- drift_detection_against_golden_runs
- model_fallback_routing
- live_dashboard
- business_outcome_metrics
@@ -0,0 +1,36 @@
version: 1.0
description: CASAN H2 Tool rate limits — prevent runaway agent loops
limits:
- tool: deploy
max_per_pipeline: 3
window: pipeline_run
note: "Max 3 deploy attempts per pipeline run before escalation"
- tool: migration
max_per_pipeline: 5
window: pipeline_run
note: "Max 5 migration runs — includes retries"
- tool: db_write
max_per_pipeline: 10
window: pipeline_run
- tool: write_code
max_per_pipeline: 50
window: pipeline_run
note: "50 write_code calls covers BE+FE implementation + up to 5 retry cycles"
- tool: external_api
max_per_hour: 100
window: rolling_60min
- tool: agent_step
max_per_pipeline: 200
window: pipeline_run
note: "Covers all 13 steps × max retries"
enforcement:
on_limit_exceeded: deny
log_denials: true
log_path: .specify/logs/audit/rate-limit-denials.jsonl
@@ -0,0 +1,62 @@
version: 1.0
description: Agent Tracking & Tracing Configuration
tracking:
enabled: true
trace:
fields:
- trace_id
- timestamp
- user_input
- processed_input
- output
- latency_ms
- tokens_input
- tokens_output
- total_tokens
- cost_estimate
- retry_count
- status
- error
correlation:
enable: true
key: trace_id
stages:
- name: input_received
log: true
- name: security_checked
log: true
- name: governance_checked
log: true
- name: llm_called
log: true
- name: output_filtered
log: true
storage:
type: file
path: ".specify/logs/trace/"
format: json
retention:
days: 30
logging:
level: info
include_input: true
include_output: true
@@ -0,0 +1,95 @@
version: 1.0
description: AI Governance Approval Workflow
workflow:
name: casan_level4_agent_action_approval
steps:
- id: STEP-001
name: Submit Prompt
actor: developer
action: submit_prompt
output: prompt_received
- id: STEP-002
name: Security Validation
actor: system
action: validate_security
input: prompt_received
output: security_checked
fail_action: block
- id: STEP-003
name: Risk Assessment
actor: system
action: evaluate_risk
input: security_checked
output: risk_level
- id: STEP-004
name: Approval Decision
actor: architect
condition:
- if: risk_level == "high"
action: require_approval
- if: risk_level == "medium"
action: optional_approval
- if: risk_level == "low"
action: auto_approve
- id: STEP-005
name: LLM Execution
actor: system
condition: approved == true
action: call_llm
output: response
- id: STEP-006
name: Audit Logging
actor: system
action: log_event
input: all
output: audit_record
approval_roles:
developer:
can_submit: true
can_approve: false
architect:
can_submit: false
can_approve: true
system:
can_execute: true
rules:
high_risk:
required_approver: architect
timeout: 10m
default_decision: deny
non_interactive_override:
env_decision: CASAN_APPROVAL_DECISION
env_approver: CASAN_APPROVER
medium_risk:
required_approver: optional
timeout: 5m
default_decision: auto_approve_with_audit
low_risk:
auto_approve: true
notifications:
on_approval_required:
channel: log
message: "Approval required for high-risk action"
on_denied:
channel: log
message: "Request denied"
@@ -0,0 +1,60 @@
version: 1.0
description: Immutable Audit Log Configuration
audit:
enabled: true
mode: append_only_hash_chain # immutable evidence with previous_record_hash + record_hash
log_fields:
- timestamp
- trace_id
- user
- action
- input_hash
- output_hash
- status
- approval_status
- risk_level
event_types:
- prompt_submitted
- prompt_blocked
- llm_called
- output_generated
- output_blocked
- approval_requested
- approval_granted
- approval_denied
- policy_violation
storage:
type: file
path: ".specify/logs/audit/audit.jsonl"
retention:
days: 90
integrity:
hash_algorithm: sha256
chain_fields:
- previous_record_hash
- record_hash
tamper_evidence: true
actions:
log:
level: info
alert:
level: high
notify: true
compliance:
standards:
- OWASP_LLM_TOP10
- NIST_AI_RMF
- ISO_42001
``
@@ -0,0 +1,98 @@
version: 1.0
description: AI Risk Registry (Governance + Security Mapping)
risks:
- id: RISK-001
name: Prompt Injection
category: security
severity: high
likelihood: high
impact: high
detection:
source: prompt-filter
mitigation:
- block malicious pattern
- validate input
owner: security_team
- id: RISK-002
name: Data Leakage
category: data
severity: high
likelihood: medium
impact: high
detection:
source: output-policy
mitigation:
- output filtering
- redact sensitive data
owner: ai_team
- id: RISK-003
name: PII Exposure
category: privacy
severity: high
likelihood: medium
impact: high
detection:
source: pii-rules
mitigation:
- mask PII
- block if critical
owner: compliance_team
- id: RISK-004
name: Hallucination Output
category: quality
severity: medium
likelihood: high
impact: medium
detection:
source: evaluation-harness
mitigation:
- flag uncertain output
- human review
owner: ai_team
- id: RISK-005
name: Unauthorized Tool Usage
category: security
severity: high
likelihood: low
impact: high
detection:
source: tool-harness
mitigation:
- whitelist tools
- enforce permission
owner: platform_team
risk_levels:
high:
action: require_approval
medium:
action: log_and_monitor
low:
action: allow
reporting:
enabled: true
frequency: daily
output: ".specify/governance/risk-report.md"
review:
cycle: weekly
owner: architect
auto_update:
enabled: true
trigger: new_action_type_detected
script: .specify/scripts/powershell/update-risk-registry.ps1
default_risk_for_unknown_action: high
note: "Unknown actions default to high-risk (fail-secure). Run update-risk-registry.ps1 to register."
@@ -0,0 +1,11 @@
{
"ai": "claude",
"ai_commands_dir": ".claude/commands",
"ai_skills": false,
"branch_numbering": "sequential",
"here": true,
"offline": true,
"preset": null,
"script": "sh",
"speckit_version": "0.4.0"
}
@@ -0,0 +1,9 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt/dFRVvIFBO+YIs0gMDN
XsBh6YSDkdbDe/jcISLCqplACnlyBRPcv6vaC4+VA8w/z9Q82x9GPei2fFPpF+tW
dEwZGyrBWF0b337myfZj/h+pTrOWoJjvJFrPcQhCM6iMxLyORt3HU6OpL2EwYMkW
Y9kGxBKUrrW5nXcnb+4zsOtPauVFDjij7JuIEnVTOpPkljPI+/MF7Ik/mrHuUgUM
JatzQ1XvwPg42qvyDXbSR1IvCMB71v+0kkibv0SsTm5CogMFwS/BtOecnYJG40i0
hB0GbKABGKqr8tYSrfSuXSuY/14JuzWtIJRB1pNyIL+bpuuU+isiEeBvEEWpqJvP
xwIDAQAB
-----END PUBLIC KEY-----
@@ -0,0 +1,55 @@
version: 1.0
bundle_id: casan-okr-harness-policy
owner: ai-governance-board
description: Central policy bundle for CASAN Level 5 governance
policies:
- id: h4-prompt-filter
path: .specify/security/prompt-filter.yaml
owner: security-team
required: true
- id: h4-output-policy
path: .specify/security/output-policy.yaml
owner: security-team
required: true
- id: h5-approval-flow
path: .specify/governance/approval-flow.yaml
owner: governance-team
required: true
- id: h5-risk-registry
path: .specify/governance/risk-registry.yaml
owner: governance-team
required: true
- id: h6-metrics
path: .specify/agentops/metrics.yaml
owner: agentops-team
required: true
- id: l5-tool-registry
path: .specify/level5/tool-registry.yaml
owner: platform-team
required: true
- id: l5-fallback
path: .specify/level5/model-fallback.yaml
owner: platform-team
required: true
- id: l5-drift
path: .specify/level5/drift-policy.yaml
owner: quality-team
required: true
approval:
signing_required: true
approver_group: ai-governance-board
verification_script: .specify/scripts/bash/sign-policy-bundle.sh
evidence:
manifest: .specify/level5/central-governance/policy-manifest.json
signature: .specify/level5/central-governance/policy-manifest.sig
public_key: .specify/level5/central-governance/policy-public.pem
@@ -0,0 +1,46 @@
{
"bundle_id": "casan-okr-harness-policy",
"files": [
{
"bytes": 2856,
"path": ".specify/security/prompt-filter.yaml",
"sha256": "4bd0b747aed9751d0a39b0021656884672624c746fca972ccc38ce1856403cde"
},
{
"bytes": 1230,
"path": ".specify/security/output-policy.yaml",
"sha256": "1c890c7ca45ad56a9ba7df8d933184021766eabcd701bb92860e05416cae25c3"
},
{
"bytes": 1935,
"path": ".specify/governance/approval-flow.yaml",
"sha256": "5ee774fd595fae08429fa7832fccf1c3fa7b607446e5f8b602b36dfabedbe5b9"
},
{
"bytes": 2003,
"path": ".specify/governance/risk-registry.yaml",
"sha256": "a37a7eb3db26389f3bf024637ed88a95bf159beec0f60b25fa17c969bb762440"
},
{
"bytes": 1053,
"path": ".specify/agentops/metrics.yaml",
"sha256": "3b8b7d1b71dd654bf600c5366b012daf7dea6415e71b50688872526bfac5af33"
},
{
"bytes": 2308,
"path": ".specify/level5/tool-registry.yaml",
"sha256": "c7f0081617b754b84a70ee6386277f3e8cd827ba8685d0245bc2b4f979951837"
},
{
"bytes": 722,
"path": ".specify/level5/model-fallback.yaml",
"sha256": "731f98e9ca8182af9183de3711c0376c2e5daf38c769f27a5556d438aa53e2d7"
},
{
"bytes": 389,
"path": ".specify/level5/drift-policy.yaml",
"sha256": "e81ea7435052b1cfbb3d296789a8d6c8bd839675150298a879ad7f5f1d853312"
}
],
"generated_at": "2026-06-29T17:01:14Z"
}
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCfM+xN7sifm+ZQ
SBk1tzCCYIzglq/t8bRB8XIkkAtPi8THuSiGFdWxL2ntuIloTiJFxqJuV2A91ksx
XwKZHd5Fu1FWjzCaWm9XI1R0oC6CKQSZrHyjzZECH41wSGIzo3IRkDvV52xFgFY2
hsZltNwKJdNUWW4EYq7OS3dlEVCp/Ly3SsTaC2rTaGpAmeODh4gYTeeyx433u49T
0ScFiTnN9Z5dUDU1meYbbVqG8RJdipFfaX43gteNFnteLJz+2k4m7RNr0L8exVXU
jqifTCPACC3uLASeRyZSHSNMINQMmmXLX72cfkLP6IMbWzYGzeIOuiuDbFLULCda
pSuoH8ONAgMBAAECggEACuTOLC0FPGq6EU9FrlfJMXqy2SFmi3UpHQQNB/R1Wa2a
OwJwA/DVjQjOCk7A1ErYbIbhkYRYGRMjVQ8HyftxvLrLH42vyTgb+033Fv3pAM4W
uv9DDiBgZlFLZ94EHj12bj484e+yiHT2MUf6pnYAQo+BDPoLnbA0/vfKBTfwmZkO
mJFaEGAP3pCqnXpPf4MVswzaziLWDjBDe8B6Ad4gaR9Sqyjrzg3blbg826tE1por
aX9V0e2w/2qJQJ/xJvvRxoro7ipLtm+sUH0E0dIsLc6ZZuHDpTO5HfIMdGJwqGHN
sYJShGYJrY9PgpczQihF0+5m1C0KlL26Gmo3EmlIEQKBgQDR156Xv282iqfRWu2R
Car4u3J3WRfr6ZxFMeYAXlhBYZl4rKsHB+ZBFaXbiS7EXDU0FUMdw7taIj8JR92O
hCBTvPlVI0/kWMPuqoYQkLAPvWXE4LKhDAxWwsbRuzSuoESHxbBBjdY1zBSr5dc5
xQV1QD4Zyjpa61St40dLhDXDCQKBgQDCOMHrElNZYezMuPq7An2zSlRwm5VOQJFO
3qVDdiz8sMN749/Wd9FoIu57JK8foW3pDdMvX0jvfyIwg+bjYlLz217WVylSJ5I2
8j3zi7aJjO/OcAXYp9DI5lrG6MEoi2RkJ8O/mvgxzQ36b7sU3nPL7Y36r1r4PGW2
QRa4HbqJZQKBgEalgQ81tAt4wucI5dJenuD+uprTs+oIzEO/eN/G44+ffDvUZgAH
+M0QrLYsiUIyiPbv8ZijvP+rZ+2H9F92vCt1A7xWFx+Zgt1nK5uIGr4x1YFi5MCz
fbP3/1e83w+rhWbwd1aamfOisZLt3nfkjOHcrwufueXCNNAWJHqYGEGBAoGAXAJV
Y4536+UnMEkFhuiEMhapb/tUvRB7hhiVxCLM5xy6TwvB87EphCLgRXg4ekyYbAq/
nDaQr9zTjpY+IERpNdsbz4DVM3VJoDwcOy9Z10nsJSkOq/e8QiBWqtqC/zkh4wyv
gCrjH3yb/aNGxnkn9FjipPrDpsu+B5xILgrvXr0CgYEAjO63m7ReluoYR3C4gcuy
Gt1Ey+DFnvqmCK7NhMWKXTb1E6cxRWWYZCI3djNcCcsWO1U2yGXaALIEnlaJTbIx
JHyKApxlq6a3fi/BSOFjOpznKtm2DJMk7TSI3avHkYzjcfpD2Cnvb1OWw1WX0y+H
1kXxn9ReB+kO8tGv1cH9Oh4=
-----END PRIVATE KEY-----
@@ -0,0 +1,9 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnzPsTe7In5vmUEgZNbcw
gmCM4Jav7fG0QfFyJJALT4vEx7kohhXVsS9p7biJaE4iRcaibldgPdZLMV8CmR3e
RbtRVo8wmlpvVyNUdKAugikEmax8o82RAh+NcEhiM6NyEZA71edsRYBWNobGZbTc
CiXTVFluBGKuzkt3ZRFQqfy8t0rE2gtq02hqQJnjg4eIGE3nsseN97uPU9EnBYk5
zfWeXVA1NZnmG21ahvESXYqRX2l+N4LXjRZ7Xiyc/tpOJu0Ta9C/HsVV1I6on0wj
wAgt7iwEnkcmUh0jTCDUDJply1+9nH5Cz+iDG1s2Bs3iDrorg2xS1CwnWqUrqB/D
jQIDAQAB
-----END PUBLIC KEY-----
@@ -0,0 +1,20 @@
version: 1.0
description: CASAN Level 5 drift detection policy
golden_dir: .specify/level5/golden-runs
evidence_dir: docs/output/casan/level5-evidence
thresholds:
minimum_similarity: 0.85
maximum_length_delta_ratio: 0.30
actions:
pass: allow
warn: require_review
fail: block_or_fallback
metrics:
- similarity_ratio
- length_delta_ratio
- golden_hash
- candidate_hash
@@ -0,0 +1,7 @@
Generate a safe OKR plan for employee ***MASKED_EMAIL***.
Expected sections:
- Objective
- Key Results
- Security gate
- Governance decision
- AgentOps metrics
@@ -0,0 +1,24 @@
{
"package": "fpt-casan-sdd-harness",
"version": "1.0.0",
"capabilities": [
"H4-security",
"H5-governance",
"H6-agentops",
"L5-drift-detection",
"L5-model-fallback",
"L5-tool-registry",
"L5-rollback",
"L5-business-feedback"
],
"compatibility": {
"speckit": ">=1.0.0",
"shell": "bash",
"python": ">=3.9"
},
"entrypoints": {
"wrapper": ".specify/scripts/bash/casan-harness.sh",
"test": ".specify/tests/run-casan4-harness-tests.sh",
"dashboard": "docs/output/casan/agentops-dashboard.html"
}
}
@@ -0,0 +1,27 @@
version: 1.0
description: CASAN Level 5 business KPI feedback schema
kpis:
- id: cycle_time_minutes
direction: lower_is_better
baseline: 180
target: 90
- id: review_rejection_rate
direction: lower_is_better
baseline: 0.30
target: 0.10
- id: defect_leakage_rate
direction: lower_is_better
baseline: 0.15
target: 0.05
- id: manual_rework_hours
direction: lower_is_better
baseline: 12
target: 4
evidence:
input: docs/output/casan/level5-evidence/business-kpi-input.json
output: docs/output/casan/level5-evidence/business-kpi-report.json
@@ -0,0 +1,33 @@
version: 1.0
description: CASAN Level 5 model/tool fallback policy
routes:
- step_pattern: "review|test|security"
primary: primary-model
fallback: verifier-model
max_attempts: 2
fallback_on:
- nonzero_exit
- empty_output
- timeout
- step_pattern: "implement|write_code|deploy"
primary: primary-model
fallback: conservative-model
max_attempts: 1
fallback_on:
- nonzero_exit
- policy_reject
governance_required: true
default_route:
primary: primary-model
fallback: fallback-model
max_attempts: 2
fallback_on:
- nonzero_exit
- empty_output
evidence:
trace_dir: .specify/logs/trace
fallback_log: .specify/logs/level5/fallback.jsonl
@@ -0,0 +1,26 @@
{
"registry": "fpt-casan-harness-adoption",
"projects": [
{
"project_id": "AINative_OKR_CASAN4",
"domain": "SDD OKR",
"harness_package": "fpt-casan-sdd-harness",
"harness_version": "1.0.0",
"status": "active"
},
{
"project_id": "CASAN_DEMO_PROJECT_A",
"domain": "Demo SDLC",
"harness_package": "fpt-casan-sdd-harness",
"harness_version": "1.0.0",
"status": "registered"
},
{
"project_id": "CASAN_DEMO_PROJECT_B",
"domain": "Demo QA",
"harness_package": "fpt-casan-sdd-harness",
"harness_version": "1.0.0",
"status": "registered"
}
]
}
@@ -0,0 +1,12 @@
{
"provider": "sample-provider",
"model": "sample-model-large",
"run_id": "provider-run-001",
"step": "speckit.implement",
"input_tokens": 1842,
"output_tokens": 936,
"total_tokens": 2778,
"cost_usd": 0.08334,
"latency_ms": 4210,
"status": "success"
}
@@ -0,0 +1,90 @@
version: 1.0
description: CASAN Level 5 tool registry with ownership, schema, risk, idempotency, and rollback metadata
tools:
- id: agent_step
owner: ai-platform
risk_level: low
side_effect: false
idempotency_required: false
timeout_seconds: 600
- id: write_code
owner: engineering
risk_level: high
side_effect: true
idempotency_required: true
timeout_seconds: 900
allowed_agents: "boss,implement-agent"
rollback:
strategy: git_patch_reverse
script: .specify/scripts/bash/rollback-manager.sh
- id: migration
owner: database-owner
risk_level: high
side_effect: true
idempotency_required: true
timeout_seconds: 900
allowed_agents: "boss,implement-agent"
rollback:
strategy: migration_down_or_restore
script: .specify/scripts/bash/rollback-manager.sh
- id: deploy
owner: release-manager
risk_level: high
side_effect: true
idempotency_required: true
timeout_seconds: 1200
rate_limit_per_run: 2
allowed_agents: "boss,release-manager"
rollback:
strategy: previous_release
script: .specify/scripts/bash/rollback-manager.sh
- id: external_api
owner: platform-team
risk_level: medium
side_effect: true
idempotency_required: true
timeout_seconds: 120
allowed_agents: "boss,implement-agent"
rollback:
strategy: compensating_request
script: .specify/scripts/bash/rollback-manager.sh
- id: db_write
owner: database-owner
risk_level: high
side_effect: true
idempotency_required: true
timeout_seconds: 300
allowed_agents: "boss,implement-agent"
rollback:
strategy: restore_from_backup
script: .specify/scripts/powershell/rollback-manager.ps1
- id: write_file
owner: engineering
risk_level: medium
side_effect: true
idempotency_required: false
timeout_seconds: 60
allowed_agents: "boss,implement-agent,design-agent"
rollback:
strategy: delete_or_restore
script: .specify/scripts/bash/rollback-manager.sh
level5_gate:
required_fields:
- id
- owner
- risk_level
- side_effect
- idempotency_required
- timeout_seconds
side_effecting_tools_require:
- idempotency_key
- audit_record
- rollback_strategy
@@ -0,0 +1,2 @@
¶J°ÅLºsܘã•ö†ƒÍAºk,ÙÝ^†B†Ã’æM#n¸NcBg{ûYT®Ã/+Ú®øõ5Љ␍¾ê<g©È¤Î:'¨ôjL
Ò:.J3"Ý•ŠÔS � 6¢ÝGܰ�¤3Ua˜n^-c»à-á2·«ÙŽØ¨zÝWí…ü™‰Ïrˆô©z,±�žå=•Ñ©Uų&Ëd“NÍÔ%:–ž_YðNAÿnl~Aó*/§ˆ‰õ­38¢íuVåÖÊÙfGLƒÖº]F»çôè'ÁFÌL (@f` Çs—j¨$÷é¡3´bê^%´÷/Ìí Ï—Á�Íù4
@@ -0,0 +1 @@
174f322c1957a0d313ac09b8d555248fd9ddb208fe813451cd565359660d5df3
@@ -0,0 +1,8 @@
{"timestamp":"2026-06-29T17:01:07Z","trace_id":"24c9d9fc-916e-4227-87a5-80ac15fe231f","harness":"H5-governance","action":"deploy","actor":"developer","risk_level":"high","decision":"denied","approval_status":"approval_required","approver":"","input_hash":"3c1cf354bd8e23e98738dd97e726a7c751cf9c7574454d52b9698bfbb9000bff","output_hash":"3c1cf354bd8e23e98738dd97e726a7c751cf9c7574454d52b9698bfbb9000bff","previous_record_hash":"","record_hash":"1fe2871313fb09b9d28d5b60e15da3ed2fc84347b486ff6643b7fa6b859e8f86"}
{"timestamp":"2026-06-29T17:01:07Z","trace_id":"f8816605-85f8-4d98-9616-b32c1eb93b10","harness":"H5-governance","action":"deploy","actor":"developer","risk_level":"high","decision":"approved","approval_status":"human_approved","approver":"architect@example.local","input_hash":"3c1cf354bd8e23e98738dd97e726a7c751cf9c7574454d52b9698bfbb9000bff","output_hash":"3c1cf354bd8e23e98738dd97e726a7c751cf9c7574454d52b9698bfbb9000bff","previous_record_hash":"1fe2871313fb09b9d28d5b60e15da3ed2fc84347b486ff6643b7fa6b859e8f86","record_hash":"0d922f790616f1cd2a33cbfd21a042ebbc25532ff7b9785642a145bd298cd60a"}
{"timestamp":"2026-06-29T17:01:10Z","trace_id":"fd1f03a5-d72a-4a54-a376-3f183bb1d47a","harness":"H5-governance","action":"agent_step","actor":"developer","risk_level":"low","decision":"approved","approval_status":"auto_approved","approver":"","input_hash":"0649852ef55d103b9b1134daf5324e92a3e2254386dc137787ca9432b2e4b9bf","output_hash":"0649852ef55d103b9b1134daf5324e92a3e2254386dc137787ca9432b2e4b9bf","previous_record_hash":"0d922f790616f1cd2a33cbfd21a042ebbc25532ff7b9785642a145bd298cd60a","record_hash":"b411874dd4146d21a63c4140e1e08110ba398d11fff170472b20198867e89133"}
{"timestamp":"2026-06-29T17:01:11Z","trace_id":"73438130-f67c-4279-94c2-8a9e780691e8","harness":"H5-governance","action":"agent_step","actor":"developer","risk_level":"low","decision":"approved","approval_status":"auto_approved","approver":"","input_hash":"0649852ef55d103b9b1134daf5324e92a3e2254386dc137787ca9432b2e4b9bf","output_hash":"0649852ef55d103b9b1134daf5324e92a3e2254386dc137787ca9432b2e4b9bf","previous_record_hash":"b411874dd4146d21a63c4140e1e08110ba398d11fff170472b20198867e89133","record_hash":"8615f2bd2899912d2804fe93a4fe68b14471fd339a3ade74aef9720f8d031906"}
{"timestamp":"2026-06-29T17:01:19Z","trace_id":"8bb3e146-0e09-4abe-b45c-efe65f38404f","harness":"H5-governance","action":"deploy","actor":"alice","risk_level":"high","decision":"denied","approval_status":"separation_of_duties_violation","approver":"alice","input_hash":"ec9c2145d75be1bf0080711b5bd106465a8307147205ea249bfa1faa0bf05bbb","output_hash":"ec9c2145d75be1bf0080711b5bd106465a8307147205ea249bfa1faa0bf05bbb","previous_record_hash":"8615f2bd2899912d2804fe93a4fe68b14471fd339a3ade74aef9720f8d031906","record_hash":"f3b567697e9169fe3f3d7d314b49d1a1e7f7b56f2ef3c7e375ad2be5bf2f8d0e"}
{"timestamp":"2026-06-29T17:01:19Z","trace_id":"ac7cf912-c123-4c02-94ad-5fd14bd8e5d6","harness":"H5-governance","action":"deploy","actor":"alice","risk_level":"high","decision":"approved","approval_status":"human_approved","approver":"bob","input_hash":"ec9c2145d75be1bf0080711b5bd106465a8307147205ea249bfa1faa0bf05bbb","output_hash":"ec9c2145d75be1bf0080711b5bd106465a8307147205ea249bfa1faa0bf05bbb","previous_record_hash":"f3b567697e9169fe3f3d7d314b49d1a1e7f7b56f2ef3c7e375ad2be5bf2f8d0e","record_hash":"534b9f4d08ffefe8d22dfad9137d2c739655e2ddb575b1831d0ef06feee3b047"}
{"timestamp":"2026-06-29T17:01:21Z","trace_id":"26c1c13f-a278-414f-8f36-1dcaed0f72d3","harness":"H5-governance","action":"write_code","actor":"developer","risk_level":"medium","decision":"approved","approval_status":"policy_auto_approved_with_audit","approver":"","input_hash":"d62056ac97257e952127e4ee599f12b633725abe0c5a7c8fbaa82d6c29561190","output_hash":"d62056ac97257e952127e4ee599f12b633725abe0c5a7c8fbaa82d6c29561190","previous_record_hash":"534b9f4d08ffefe8d22dfad9137d2c739655e2ddb575b1831d0ef06feee3b047","record_hash":"779e8b997802a71f7522c8eb434c2f413a826883c6e1b067601a00af83164511"}
{"timestamp":"2026-06-29T17:01:22Z","trace_id":"68de2633-d730-423c-99b1-189c2b3bea55","harness":"H5-governance","action":"write_code","actor":"developer","risk_level":"medium","decision":"approved","approval_status":"policy_auto_approved_with_audit","approver":"","input_hash":"d62056ac97257e952127e4ee599f12b633725abe0c5a7c8fbaa82d6c29561190","output_hash":"d62056ac97257e952127e4ee599f12b633725abe0c5a7c8fbaa82d6c29561190","previous_record_hash":"779e8b997802a71f7522c8eb434c2f413a826883c6e1b067601a00af83164511","record_hash":"174f322c1957a0d313ac09b8d555248fd9ddb208fe813451cd565359660d5df3"}
@@ -0,0 +1,20 @@
{"timestamp":"2026-06-29T17:01:05Z","trace_id":"8f5f5208-a8cd-43b0-930c-bdcf169a4ccd","harness":"H4-security","mode":"input","status":"blocked","action":"block","risk_level":"high","input_hash":"035b92ef321008bd61f79796ffc237b8476e2e008b8f2ee2c5b6cb781bd7d69c","output_hash":"035b92ef321008bd61f79796ffc237b8476e2e008b8f2ee2c5b6cb781bd7d69c"}
{"timestamp":"2026-06-29T17:01:05Z","trace_id":"0d29681f-4fc6-4e89-8fab-98a6e560a9f8","harness":"H4-security","mode":"input","status":"pass","action":"allow","risk_level":"low","input_hash":"c9e8e44c7c4b03acf3a39c0343d25c371d54668be1015cd743d5c289c62ac587","output_hash":"ae9d8e18b2c01ad609f3a9e8e36072c116ea6f088c8266a1b56323418c3b7066"}
{"timestamp":"2026-06-29T17:01:06Z","trace_id":"c60dc00a-9615-44cc-8c2e-4b4196201739","harness":"H4-security","mode":"input","status":"blocked","action":"block","risk_level":"high","input_hash":"ccae83f7a01fac243ce8287d541c19b19505a1dc1cbb115319788ee4e67bc7aa","output_hash":"ccae83f7a01fac243ce8287d541c19b19505a1dc1cbb115319788ee4e67bc7aa"}
{"timestamp":"2026-06-29T17:01:06Z","trace_id":"a62b63a5-0ba1-4617-bc55-fd462234b43a","harness":"H4-security","mode":"input","status":"blocked","action":"block","risk_level":"high","input_hash":"1bb7c8b981b9830a7b0098d5a4cb2283ef2eda3ee31926f4de50db96fba1e585","output_hash":"06be335c7385a9ae5cbc07c8112991635742098ff4418756637ed598b98ddb92"}
{"timestamp":"2026-06-29T17:01:09Z","trace_id":"5f48b3bd-0ac6-4b39-b65a-1e44455ab65c","harness":"H4-security","mode":"input","status":"pass","action":"allow","risk_level":"low","input_hash":"267c99d13eb60ecfc59c51625918582143c1e580f32d1181985058d53cd8e4a2","output_hash":"0649852ef55d103b9b1134daf5324e92a3e2254386dc137787ca9432b2e4b9bf"}
{"timestamp":"2026-06-29T17:01:11Z","trace_id":"b1269a1e-bfe4-414e-92c5-42c546f93874","harness":"H4-security","mode":"output","status":"pass","action":"allow","risk_level":"low","input_hash":"0649852ef55d103b9b1134daf5324e92a3e2254386dc137787ca9432b2e4b9bf","output_hash":"0649852ef55d103b9b1134daf5324e92a3e2254386dc137787ca9432b2e4b9bf"}
{"timestamp":"2026-06-29T17:01:11Z","trace_id":"7b128544-88d6-4f3f-b89e-567a80c277da","harness":"H4-security","mode":"input","status":"pass","action":"allow","risk_level":"low","input_hash":"267c99d13eb60ecfc59c51625918582143c1e580f32d1181985058d53cd8e4a2","output_hash":"0649852ef55d103b9b1134daf5324e92a3e2254386dc137787ca9432b2e4b9bf"}
{"timestamp":"2026-06-29T17:01:12Z","trace_id":"bcedbd04-ffb8-49ea-b2aa-d1839a0072a9","harness":"H4-security","mode":"output","status":"pass","action":"allow","risk_level":"low","input_hash":"0649852ef55d103b9b1134daf5324e92a3e2254386dc137787ca9432b2e4b9bf","output_hash":"0649852ef55d103b9b1134daf5324e92a3e2254386dc137787ca9432b2e4b9bf"}
{"timestamp":"2026-06-29T17:01:15Z","trace_id":"1a060656-550a-4cc3-8190-6b74d02cfc66","harness":"H4-security","mode":"input","status":"blocked","action":"block","risk_level":"high","input_hash":"f71ca2245e40db381bc39caa0264e22f222758a67683c5b82721b921f91a38cc","output_hash":"f71ca2245e40db381bc39caa0264e22f222758a67683c5b82721b921f91a38cc"}
{"timestamp":"2026-06-29T17:01:15Z","trace_id":"7d738dd4-0c71-4ee2-88be-314db2d2793d","harness":"H4-security","mode":"input","status":"blocked","action":"block","risk_level":"high","input_hash":"319e57557373ad701207bafb3ce13d3578fd16af6ce3ee0cc71ad35d35a4c156","output_hash":"319e57557373ad701207bafb3ce13d3578fd16af6ce3ee0cc71ad35d35a4c156"}
{"timestamp":"2026-06-29T17:01:16Z","trace_id":"b7196c7d-3cec-4a33-80dc-6df08709e845","harness":"H4-security","mode":"input","status":"blocked","action":"block","risk_level":"high","input_hash":"0ecb593740a007ca236c2bdfa0f5fd8f238a8e90576f0973a3f131667244b32d","output_hash":"0ecb593740a007ca236c2bdfa0f5fd8f238a8e90576f0973a3f131667244b32d"}
{"timestamp":"2026-06-29T17:01:16Z","trace_id":"52d06637-5282-4d6f-8757-7ab7e0e0d69a","harness":"H4-security","mode":"input","status":"blocked","action":"block","risk_level":"high","input_hash":"eee04d186d963d68cbe0bf7a7bbb5098bbc51b3489fe0c322ebac58380475cdc","output_hash":"eee04d186d963d68cbe0bf7a7bbb5098bbc51b3489fe0c322ebac58380475cdc"}
{"timestamp":"2026-06-29T17:01:17Z","trace_id":"a4009f85-52af-41e6-85d3-7c904bd46115","harness":"H4-security","mode":"input","status":"blocked","action":"block","risk_level":"high","input_hash":"9b3b6f8d8e3bb7db8b4039996e14c04d9ac3453f1968934975c7fb33be3b4470","output_hash":"9b3b6f8d8e3bb7db8b4039996e14c04d9ac3453f1968934975c7fb33be3b4470"}
{"timestamp":"2026-06-29T17:01:17Z","trace_id":"b9aeddaf-d337-4e65-bd60-535d4e27ac91","harness":"H4-security","mode":"input","status":"blocked","action":"block","risk_level":"high","input_hash":"d74b29905bc2219494c55c82b3f0c0e24eee5ea8c5c0279e5b58c4aab57bf8da","output_hash":"1318f233f3d8afdd23b9dbe5eb7a338758c39c5b9e86cc0a653779858ed60585"}
{"timestamp":"2026-06-29T17:01:18Z","trace_id":"0a1578fd-5e42-4796-9313-c00fd6936f2f","harness":"H4-security","mode":"input","status":"blocked","action":"block","risk_level":"high","input_hash":"ad1d539f1df96460a8045da532dc6b4782f117a1a131c15d986abfc95db583eb","output_hash":"0c1bde32600f6661e10e21b6da145740b31d9a380d6324e9831a24540864a479"}
{"timestamp":"2026-06-29T17:01:18Z","trace_id":"db192f2a-28e9-4009-b529-de5d74029af9","harness":"H4-security","mode":"output","status":"blocked","action":"block","risk_level":"high","input_hash":"0e3b85a844f6c5f282d095866d9811dd8d14048fc0f2669a4c3fb19a91d6c7eb","output_hash":"095b06c8bd180255e390c3061508d5b4cfd88527edb7f022952510c3777d2bbd"}
{"timestamp":"2026-06-29T17:01:18Z","trace_id":"f0019d48-a289-4493-be90-7bd24115796e","harness":"H4-security","mode":"input","status":"pass","action":"allow","risk_level":"low","input_hash":"11f1d3168daa61cfb1195bb8aed22b922bd13ccbbdeeec6cc5512b46e10ffedb","output_hash":"11f1d3168daa61cfb1195bb8aed22b922bd13ccbbdeeec6cc5512b46e10ffedb"}
{"timestamp":"2026-06-29T17:01:20Z","trace_id":"3e4dd63e-1d00-4043-9fb4-13cd677adfd8","harness":"H4-security","mode":"input","status":"pass","action":"allow","risk_level":"low","input_hash":"d62056ac97257e952127e4ee599f12b633725abe0c5a7c8fbaa82d6c29561190","output_hash":"d62056ac97257e952127e4ee599f12b633725abe0c5a7c8fbaa82d6c29561190"}
{"timestamp":"2026-06-29T17:01:21Z","trace_id":"1cca9feb-d7b3-4e87-89a4-5aaafa519981","harness":"H4-security","mode":"input","status":"pass","action":"allow","risk_level":"low","input_hash":"d62056ac97257e952127e4ee599f12b633725abe0c5a7c8fbaa82d6c29561190","output_hash":"d62056ac97257e952127e4ee599f12b633725abe0c5a7c8fbaa82d6c29561190"}
{"timestamp":"2026-06-29T17:01:23Z","trace_id":"14027a15-8de9-4f1e-bf3b-d7bace5feffb","harness":"H4-security","mode":"output","status":"pass","action":"allow","risk_level":"low","input_hash":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","output_hash":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"}
@@ -0,0 +1 @@
bc47939734144ff4f85267fd3393e3d948c3c526e6fb5242b190f12bd2159f29
@@ -0,0 +1,17 @@
{"timestamp": "2026-06-29T17:01:08Z", "trace_id": "98e40b76-6db7-4357-83c4-b879308cd645", "agent": "demo.agent", "step": "step-1-srs", "tool": "Bash", "command": "bash -c cp \"$CASAN_INPUT\" \"$CASAN_OUTPUT\"", "exit_code": 0, "status": "success", "previous_record_hash": "", "record_hash": "06f7a7bcca45d5cee8bcc3e6ef265073fea510367131429fc51683c9ee74e9cf"}
{"timestamp": "2026-06-29T17:01:09Z", "trace_id": "45331383-aaed-47b7-bfa1-58eba82adcd3", "agent": "demo.agent", "step": "failing-step", "tool": "Bash", "command": "bash -c exit 7", "exit_code": 7, "status": "failed", "previous_record_hash": "06f7a7bcca45d5cee8bcc3e6ef265073fea510367131429fc51683c9ee74e9cf", "record_hash": "0b2a8633e5707b365fbb574d62d757af30df52af8e74ff161fb20993b2ef3d68"}
{"timestamp": "2026-06-29T17:01:12Z", "trace_id": "c54e9272-bc63-4c83-9389-49fc7f8fb312", "agent": "wrapper.demo", "step": "wrapper-step", "tool": "Bash", "command": "bash -c cp \"$1\" \"$CASAN_OUTPUT\" _ /Users/thanhnguyen/Documents/AI/HarnessHkt/Harness_Hakathon/Output_CASAN5_REFINED/AINative_OKR_CASAN5/.specify/logs/idempotency/a695c82f9d29aba2adace11fc766fd1b0ffae9ef27514df5907bdf66a69e0bba.output", "exit_code": 0, "status": "success", "previous_record_hash": "0b2a8633e5707b365fbb574d62d757af30df52af8e74ff161fb20993b2ef3d68", "record_hash": "a269c2e5c0093bd2b7f5cdf22f1b981507e4d76604c0b9babc835923166acde4"}
{"timestamp": "2026-06-29T17:01:13Z", "trace_id": "f9474f8d-6b89-4174-9cfb-bff333fe457b", "tool": "deploy", "agent": "release-manager", "idempotency_key": "", "decision": "denied", "reason": "missing_idempotency_key", "risk_level": "high", "owner": "release-manager", "previous_record_hash": "a269c2e5c0093bd2b7f5cdf22f1b981507e4d76604c0b9babc835923166acde4", "record_hash": "f198fabaaad1eb6509a991b90646b65e31800bf4f5f51fd91ced3d494d5d9cd4"}
{"timestamp": "2026-06-29T17:01:14Z", "trace_id": "eb92740f-4926-4d64-947c-baa40740e3e8", "tool": "deploy", "agent": "release-manager", "idempotency_key": "deploy-demo-001", "decision": "approved", "reason": "registered", "risk_level": "high", "owner": "release-manager", "previous_record_hash": "f198fabaaad1eb6509a991b90646b65e31800bf4f5f51fd91ced3d494d5d9cd4", "record_hash": "0dd6a2f6ffa7156eb06a1be0bc7fd936ff1eca3954daa87856241f023012e3a6"}
{"timestamp": "2026-06-29T17:01:14Z", "trace_id": "652315d1-daf6-4b02-99fb-49f56df435f8", "tool": "deploy", "agent": "design-agent", "idempotency_key": "deploy-demo-002", "decision": "denied", "reason": "unauthorized_agent", "risk_level": "high", "owner": "release-manager", "previous_record_hash": "0dd6a2f6ffa7156eb06a1be0bc7fd936ff1eca3954daa87856241f023012e3a6", "record_hash": "d4e5ebe2e47cd6f0aa4cb61cef8da7f22bcdcf53385c37fc14166dcd5e29cf75"}
{"timestamp": "2026-06-29T17:01:20Z", "trace_id": "2012f10a-b890-4fa0-8282-cef670650e8f", "tool": "deploy", "agent": "design-agent", "idempotency_key": "k1", "decision": "denied", "reason": "unauthorized_agent", "risk_level": "high", "owner": "release-manager", "previous_record_hash": "d4e5ebe2e47cd6f0aa4cb61cef8da7f22bcdcf53385c37fc14166dcd5e29cf75", "record_hash": "2517a24c6eb274f2028cd24447561ee0993edc5efc70f801292314f18ae54242"}
{"timestamp": "2026-06-29T17:01:20Z", "trace_id": "63d0fe70-ce70-472d-a7bd-44a44362c44f", "tool": "deploy", "agent": "", "idempotency_key": "k2", "decision": "denied", "reason": "missing_agent_identity", "risk_level": "high", "owner": "release-manager", "previous_record_hash": "2517a24c6eb274f2028cd24447561ee0993edc5efc70f801292314f18ae54242", "record_hash": "1a20100bfe3413d0cb35810f99377efafe275ac172e44bfeaadecf8396ccba90"}
{"timestamp": "2026-06-29T17:01:20Z", "trace_id": "2f907ff0-1a17-4bcc-b48f-4f04074742f8", "tool": "deploy", "agent": "release-manager", "idempotency_key": "k3", "decision": "approved", "reason": "registered", "risk_level": "high", "owner": "release-manager", "previous_record_hash": "1a20100bfe3413d0cb35810f99377efafe275ac172e44bfeaadecf8396ccba90", "record_hash": "31302db909d12368e7860e8aec2d2cfe6636d1d50f7cd4cba87731ff667e765b"}
{"timestamp": "2026-06-29T17:01:21Z", "trace_id": "040d773f-ad80-4df4-9287-127870281d44", "tool": "write_code", "agent": "design-agent", "idempotency_key": "d6d750d0ee93e4b9f9b917a42e3f7f6e8725c63bf412594778810bd91c175084", "decision": "denied", "reason": "unauthorized_agent", "risk_level": "high", "owner": "engineering", "previous_record_hash": "31302db909d12368e7860e8aec2d2cfe6636d1d50f7cd4cba87731ff667e765b", "record_hash": "a1dcd2da3ee3a96b1fcdcb451ae1cb5a3e403088fc2ca66b40b281bcc4f38023"}
{"timestamp": "2026-06-29T17:01:22Z", "trace_id": "7886aec9-93b1-4a0a-9356-75860899b993", "tool": "write_code", "agent": "implement-agent", "idempotency_key": "d6d750d0ee93e4b9f9b917a42e3f7f6e8725c63bf412594778810bd91c175084", "decision": "approved", "reason": "registered", "risk_level": "high", "owner": "engineering", "previous_record_hash": "a1dcd2da3ee3a96b1fcdcb451ae1cb5a3e403088fc2ca66b40b281bcc4f38023", "record_hash": "e9b39b5030c80e0f08ba8cb809a6901338e1b7d1df018b212a3f9b78f7e53914"}
{"timestamp": "2026-06-29T17:01:22Z", "trace_id": "997d7a8d-7d82-4b85-acd5-7b95ceaa0aaf", "agent": "unknown-agent", "step": "unknown-step", "tool": "Bash", "command": "bash -c echo wrote", "exit_code": 0, "status": "success", "previous_record_hash": "e9b39b5030c80e0f08ba8cb809a6901338e1b7d1df018b212a3f9b78f7e53914", "record_hash": "e50cbd89d48081c9a170eb5370f4c358156ecdd3a668fa2d9d22f535ac0398b7"}
{"timestamp": "2026-06-29T17:01:23Z", "trace_id": "d5857d18-83ac-4dbf-8d47-aebb4549255d", "agent": "adv", "step": "step-1-srs", "tool": "Bash", "command": "bash -c cp \"$CASAN_INPUT\" \"$CASAN_OUTPUT\"", "exit_code": 0, "status": "success", "previous_record_hash": "e50cbd89d48081c9a170eb5370f4c358156ecdd3a668fa2d9d22f535ac0398b7", "record_hash": "dcb18cf8160d32c1254db3d741a25b0b56f8aec6e172a78018b9a90be2aae015"}
{"timestamp": "2026-06-29T17:01:24Z", "trace_id": "d5d03ef1-b24b-4168-bae9-27dd16efc987", "agent": "adv", "step": "step-1-srs", "tool": "Bash", "command": "bash -c cp \"$CASAN_INPUT\" \"$CASAN_OUTPUT\"", "exit_code": 0, "status": "success", "previous_record_hash": "dcb18cf8160d32c1254db3d741a25b0b56f8aec6e172a78018b9a90be2aae015", "record_hash": "ac0d4e2aaaea6e203ac58f4d84045193224c101ef287467105b8237da83636f8"}
{"timestamp": "2026-06-29T17:01:25Z", "trace_id": "bdf83688-3a1b-4b7f-a332-5f2fc3327963", "tool": "deploy", "agent": "release-manager", "idempotency_key": "k1", "decision": "approved", "reason": "registered", "risk_level": "high", "owner": "release-manager", "previous_record_hash": "ac0d4e2aaaea6e203ac58f4d84045193224c101ef287467105b8237da83636f8", "record_hash": "13aadef598910b49887efefc41827097e91b7e355a7dac4a9b5ceb42d50c4b26"}
{"timestamp": "2026-06-29T17:01:25Z", "trace_id": "eb237a82-8b09-477e-951c-f437650407a5", "tool": "deploy", "agent": "release-manager", "idempotency_key": "k2", "decision": "approved", "reason": "registered", "risk_level": "high", "owner": "release-manager", "previous_record_hash": "13aadef598910b49887efefc41827097e91b7e355a7dac4a9b5ceb42d50c4b26", "record_hash": "f65b82fbf62b1be50a32b6f240f3766d4b1f1985e072603a5c484ae2b105e497"}
{"timestamp": "2026-06-29T17:01:25Z", "trace_id": "45e32db0-81ae-46d8-a02b-41a19f29da8a", "tool": "deploy", "agent": "release-manager", "idempotency_key": "k3", "decision": "denied", "reason": "rate_limit_exceeded(limit=2)", "risk_level": "high", "owner": "release-manager", "previous_record_hash": "f65b82fbf62b1be50a32b6f240f3766d4b1f1985e072603a5c484ae2b105e497", "record_hash": "bc47939734144ff4f85267fd3393e3d948c3c526e6fb5242b190f12bd2159f29"}
@@ -0,0 +1,9 @@
{"timestamp":"2026-06-29T17:01:07Z","trace_id":"a01ce4c3-750c-432f-8580-87486594e061","harness":"H6-agentops","agent":"demo.agent","step":"demo-step","status":"success","exit_code":0,"latency_ms":59,"retry_count":0,"input_tokens":6,"output_tokens":6,"total_tokens":12,"cost_estimate":0.00002400,"cost_source":"word_count_estimate","hallucination_signals":0,"alerts":[],"input_hash":"2a5a257dc10475c9105ba981755390fb815f3102d3e6d4fc6f7fb161f7351bac","output_hash":"2a5a257dc10475c9105ba981755390fb815f3102d3e6d4fc6f7fb161f7351bac"}
{"timestamp":"2026-06-29T17:01:08Z","trace_id":"98e40b76-6db7-4357-83c4-b879308cd645","harness":"H6-agentops","agent":"demo.agent","step":"step-1-srs","status":"success","exit_code":0,"latency_ms":220,"retry_count":0,"input_tokens":13,"output_tokens":13,"total_tokens":26,"cost_estimate":0.00005200,"cost_source":"word_count_estimate","hallucination_signals":4,"alerts":["hallucination-suspected"],"input_hash":"666dfe86cafeb8150bcc28d0b5cb2c43e1149dddaa126b5fbc8adee318f46d57","output_hash":"666dfe86cafeb8150bcc28d0b5cb2c43e1149dddaa126b5fbc8adee318f46d57"}
{"timestamp":"2026-06-29T17:01:08Z","trace_id":"038975e8-390d-4647-b55d-11d1caf78dcb","harness":"H6-agentops","agent":"demo.agent","step":"speckit.implement","status":"success","exit_code":0,"latency_ms":57,"retry_count":0,"input_tokens":6,"output_tokens":6,"total_tokens":2778,"cost_estimate":0.08334,"cost_source":"provider_telemetry","hallucination_signals":0,"alerts":[],"input_hash":"2a5a257dc10475c9105ba981755390fb815f3102d3e6d4fc6f7fb161f7351bac","output_hash":"2a5a257dc10475c9105ba981755390fb815f3102d3e6d4fc6f7fb161f7351bac"}
{"timestamp":"2026-06-29T17:01:09Z","trace_id":"45331383-aaed-47b7-bfa1-58eba82adcd3","harness":"H6-agentops","agent":"demo.agent","step":"failing-step","status":"failed","exit_code":7,"latency_ms":198,"retry_count":0,"input_tokens":6,"output_tokens":0,"total_tokens":6,"cost_estimate":0.00001200,"cost_source":"word_count_estimate","hallucination_signals":0,"alerts":["execution-failed"],"input_hash":"2a5a257dc10475c9105ba981755390fb815f3102d3e6d4fc6f7fb161f7351bac","output_hash":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"}
{"timestamp":"2026-06-29T17:01:10Z","trace_id":"a32695e8-f4da-4d5e-967d-c5d4b6283995","harness":"H6-agentops","agent":"wrapper.demo","step":"wrapper-step","status":"success","exit_code":0,"latency_ms":58,"retry_count":0,"input_tokens":7,"output_tokens":7,"total_tokens":14,"cost_estimate":0.00002800,"cost_source":"word_count_estimate","hallucination_signals":0,"alerts":[],"input_hash":"054cd5120725af9fdf9a8033f7b0f60d2d8ba3861926b00686d74700668e5116","output_hash":"054cd5120725af9fdf9a8033f7b0f60d2d8ba3861926b00686d74700668e5116"}
{"timestamp":"2026-06-29T17:01:12Z","trace_id":"c54e9272-bc63-4c83-9389-49fc7f8fb312","harness":"H6-agentops","agent":"wrapper.demo","step":"wrapper-step","status":"success","exit_code":0,"latency_ms":219,"retry_count":0,"input_tokens":7,"output_tokens":7,"total_tokens":14,"cost_estimate":0.00002800,"cost_source":"word_count_estimate","hallucination_signals":0,"alerts":[],"input_hash":"054cd5120725af9fdf9a8033f7b0f60d2d8ba3861926b00686d74700668e5116","output_hash":"054cd5120725af9fdf9a8033f7b0f60d2d8ba3861926b00686d74700668e5116"}
{"timestamp":"2026-06-29T17:01:22Z","trace_id":"997d7a8d-7d82-4b85-acd5-7b95ceaa0aaf","harness":"H6-agentops","agent":"unknown-agent","step":"unknown-step","status":"failed","exit_code":0,"latency_ms":210,"retry_count":0,"input_tokens":3,"output_tokens":0,"total_tokens":3,"cost_estimate":0.00000600,"cost_source":"word_count_estimate","hallucination_signals":0,"alerts":["execution-failed"],"input_hash":"cae0b3e41bdd7bf9fc5ab7f73d4422a65c3766f8097c90d952d07dd371605290","output_hash":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"}
{"timestamp":"2026-06-29T17:01:23Z","trace_id":"d5857d18-83ac-4dbf-8d47-aebb4549255d","harness":"H6-agentops","agent":"adv","step":"step-1-srs","status":"success","exit_code":0,"latency_ms":205,"retry_count":0,"input_tokens":13,"output_tokens":13,"total_tokens":26,"cost_estimate":0.00005200,"cost_source":"word_count_estimate","hallucination_signals":4,"alerts":["hallucination-suspected"],"input_hash":"666dfe86cafeb8150bcc28d0b5cb2c43e1149dddaa126b5fbc8adee318f46d57","output_hash":"666dfe86cafeb8150bcc28d0b5cb2c43e1149dddaa126b5fbc8adee318f46d57"}
{"timestamp":"2026-06-29T17:01:24Z","trace_id":"d5d03ef1-b24b-4168-bae9-27dd16efc987","harness":"H6-agentops","agent":"adv","step":"step-1-srs","status":"success","exit_code":0,"latency_ms":203,"retry_count":0,"input_tokens":7,"output_tokens":7,"total_tokens":14,"cost_estimate":0.00002800,"cost_source":"word_count_estimate","hallucination_signals":0,"alerts":[],"input_hash":"a97a0aba66ab15562e4d271fbcb2071c92d3662d99f2e2faac7f263ea35cfa0b","output_hash":"a97a0aba66ab15562e4d271fbcb2071c92d3662d99f2e2faac7f263ea35cfa0b"}
@@ -0,0 +1,7 @@
{
"idempotency_key": "a695c82f9d29aba2adace11fc766fd1b0ffae9ef27514df5907bdf66a69e0bba",
"timestamp": "2026-06-29T17:01:11Z",
"action": "agent_step",
"command": "no_cmd",
"output_hash": "054cd5120725af9fdf9a8033f7b0f60d2d8ba3861926b00686d74700668e5116"
}
@@ -0,0 +1 @@
Generate safe OKR plan for employee ***MASKED_EMAIL***.
@@ -0,0 +1,7 @@
{
"idempotency_key": "d6d750d0ee93e4b9f9b917a42e3f7f6e8725c63bf412594778810bd91c175084",
"timestamp": "2026-06-29T17:01:23Z",
"action": "write_code",
"command": "bash -c echo wrote",
"output_hash": "01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b"
}
@@ -0,0 +1,2 @@
{"timestamp":"2026-06-29T17:01:13Z","trace_id":"80a4d222-ec74-4eec-8bfc-4f16c87ca2e7","harness":"L5-model-fallback","primary_exit":9,"route":"fallback","final_exit":0,"output":"/Users/thanhnguyen/Documents/AI/HarnessHkt/Harness_Hakathon/Output_CASAN5_REFINED/AINative_OKR_CASAN5/docs/output/casan/level5-evidence/10-fallback-output.txt"}
{"timestamp":"2026-06-29T17:01:25Z","trace_id":"74a9bc32-7555-42d1-afaa-012637eb78ff","harness":"L5-model-fallback","primary_exit":1,"route":"fallback","final_exit":0,"output":"/var/folders/zn/qn8sqwzn18g34ddftsxgyz6r0000gn/T/tmp.JXjRzbk1NY/fb.out"}
@@ -0,0 +1,2 @@
{"timestamp": "2026-06-29T17:01:08Z", "harness": "L5-provider-telemetry", "provider": "sample-provider", "model": "sample-model-large", "run_id": "provider-run-001", "step": "speckit.implement", "input_tokens": 1842, "output_tokens": 936, "total_tokens": 2778, "cost_usd": 0.08334, "latency_ms": 4210, "status": "success"}
{"timestamp": "2026-06-29T17:01:14Z", "harness": "L5-provider-telemetry", "provider": "sample-provider", "model": "sample-model-large", "run_id": "provider-run-001", "step": "speckit.implement", "input_tokens": 1842, "output_tokens": 936, "total_tokens": 2778, "cost_usd": 0.08334, "latency_ms": 4210, "status": "success"}
@@ -0,0 +1,4 @@
{"timestamp":"2026-06-29T17:01:14Z","transaction_id":"5c7e9104-f830-4d12-93fe-30517423f95e","action":"deploy","rollback_command":"printf rolled_back > '/Users/thanhnguyen/Documents/AI/HarnessHkt/Harness_Hakathon/Output_CASAN5_REFINED/AINative_OKR_CASAN5/docs/output/casan/level5-evidence/13-rollback-marker.txt'","status":"recorded"}
{"timestamp":"2026-06-29T17:01:14Z","transaction_id":"5c7e9104-f830-4d12-93fe-30517423f95e","status":"rolled_back"}
{"timestamp": "2026-06-29T17:01:24Z", "transaction_id": "f15374ec-8fa7-49e7-9a29-5d080ce56d9a", "action": "checkpoint", "target": "/var/folders/zn/qn8sqwzn18g34ddftsxgyz6r0000gn/T/tmp.JXjRzbk1NY/rollback-target.txt", "backup": "/Users/thanhnguyen/Documents/AI/HarnessHkt/Harness_Hakathon/Output_CASAN5_REFINED/AINative_OKR_CASAN5/.specify/logs/level5/rollback-backups/f15374ec-8fa7-49e7-9a29-5d080ce56d9a.bak", "rollback_command": "cp '/Users/thanhnguyen/Documents/AI/HarnessHkt/Harness_Hakathon/Output_CASAN5_REFINED/AINative_OKR_CASAN5/.specify/logs/level5/rollback-backups/f15374ec-8fa7-49e7-9a29-5d080ce56d9a.bak' '/var/folders/zn/qn8sqwzn18g34ddftsxgyz6r0000gn/T/tmp.JXjRzbk1NY/rollback-target.txt'", "status": "recorded"}
{"timestamp":"2026-06-29T17:01:24Z","transaction_id":"f15374ec-8fa7-49e7-9a29-5d080ce56d9a","status":"rolled_back"}
@@ -0,0 +1,11 @@
{"timestamp": "2026-06-29T17:01:13Z", "trace_id": "f9474f8d-6b89-4174-9cfb-bff333fe457b", "harness": "L5-tool-registry", "tool_id": "deploy", "agent": "release-manager", "run_id": "adhoc-43181", "owner": "release-manager", "risk_level": "high", "side_effect": true, "idempotency_required": true, "idempotency_key_present": false, "decision": "denied", "reason": "missing_idempotency_key"}
{"timestamp": "2026-06-29T17:01:14Z", "trace_id": "eb92740f-4926-4d64-947c-baa40740e3e8", "harness": "L5-tool-registry", "tool_id": "deploy", "agent": "release-manager", "run_id": "adhoc-43202", "owner": "release-manager", "risk_level": "high", "side_effect": true, "idempotency_required": true, "idempotency_key_present": true, "decision": "approved", "reason": "registered"}
{"timestamp": "2026-06-29T17:01:14Z", "trace_id": "652315d1-daf6-4b02-99fb-49f56df435f8", "harness": "L5-tool-registry", "tool_id": "deploy", "agent": "design-agent", "run_id": "adhoc-43259", "owner": "release-manager", "risk_level": "high", "side_effect": true, "idempotency_required": true, "idempotency_key_present": true, "decision": "denied", "reason": "unauthorized_agent"}
{"timestamp": "2026-06-29T17:01:20Z", "trace_id": "2012f10a-b890-4fa0-8282-cef670650e8f", "harness": "L5-tool-registry", "tool_id": "deploy", "agent": "design-agent", "run_id": "adhoc-45518", "owner": "release-manager", "risk_level": "high", "side_effect": true, "idempotency_required": true, "idempotency_key_present": true, "decision": "denied", "reason": "unauthorized_agent"}
{"timestamp": "2026-06-29T17:01:20Z", "trace_id": "63d0fe70-ce70-472d-a7bd-44a44362c44f", "harness": "L5-tool-registry", "tool_id": "deploy", "agent": "", "run_id": "adhoc-45586", "owner": "release-manager", "risk_level": "high", "side_effect": true, "idempotency_required": true, "idempotency_key_present": true, "decision": "denied", "reason": "missing_agent_identity"}
{"timestamp": "2026-06-29T17:01:20Z", "trace_id": "2f907ff0-1a17-4bcc-b48f-4f04074742f8", "harness": "L5-tool-registry", "tool_id": "deploy", "agent": "release-manager", "run_id": "adhoc-45605", "owner": "release-manager", "risk_level": "high", "side_effect": true, "idempotency_required": true, "idempotency_key_present": true, "decision": "approved", "reason": "registered"}
{"timestamp": "2026-06-29T17:01:21Z", "trace_id": "040d773f-ad80-4df4-9287-127870281d44", "harness": "L5-tool-registry", "tool_id": "write_code", "agent": "design-agent", "run_id": "adhoc-45947", "owner": "engineering", "risk_level": "high", "side_effect": true, "idempotency_required": true, "idempotency_key_present": true, "decision": "denied", "reason": "unauthorized_agent"}
{"timestamp": "2026-06-29T17:01:22Z", "trace_id": "7886aec9-93b1-4a0a-9356-75860899b993", "harness": "L5-tool-registry", "tool_id": "write_code", "agent": "implement-agent", "run_id": "adhoc-46363", "owner": "engineering", "risk_level": "high", "side_effect": true, "idempotency_required": true, "idempotency_key_present": true, "decision": "approved", "reason": "registered"}
{"timestamp": "2026-06-29T17:01:25Z", "trace_id": "bdf83688-3a1b-4b7f-a332-5f2fc3327963", "harness": "L5-tool-registry", "tool_id": "deploy", "agent": "release-manager", "run_id": "adv-43456", "owner": "release-manager", "risk_level": "high", "side_effect": true, "idempotency_required": true, "idempotency_key_present": true, "decision": "approved", "reason": "registered"}
{"timestamp": "2026-06-29T17:01:25Z", "trace_id": "eb237a82-8b09-477e-951c-f437650407a5", "harness": "L5-tool-registry", "tool_id": "deploy", "agent": "release-manager", "run_id": "adv-43456", "owner": "release-manager", "risk_level": "high", "side_effect": true, "idempotency_required": true, "idempotency_key_present": true, "decision": "approved", "reason": "registered"}
{"timestamp": "2026-06-29T17:01:25Z", "trace_id": "45e32db0-81ae-46d8-a02b-41a19f29da8a", "harness": "L5-tool-registry", "tool_id": "deploy", "agent": "release-manager", "run_id": "adv-43456", "owner": "release-manager", "risk_level": "high", "side_effect": true, "idempotency_required": true, "idempotency_key_present": true, "decision": "denied", "reason": "rate_limit_exceeded(limit=2)"}
@@ -0,0 +1 @@
Generate safe OKR plan for employee ***MASKED_EMAIL***.
@@ -0,0 +1 @@
Generate safe OKR plan for employee ***MASKED_EMAIL***.
@@ -0,0 +1 @@
apply code changes
@@ -0,0 +1 @@
apply code changes
@@ -0,0 +1 @@
Generate safe OKR plan for employee ***MASKED_EMAIL***.
@@ -0,0 +1 @@
Generate safe OKR plan for employee ***MASKED_EMAIL***.
@@ -0,0 +1 @@
Generate safe OKR plan for employee ***MASKED_EMAIL***.
@@ -0,0 +1 @@
Generate safe OKR plan for employee ***MASKED_EMAIL***.
@@ -0,0 +1 @@
apply code changes
@@ -0,0 +1 @@
apply code changes
@@ -0,0 +1,22 @@
{
"trace_id": "038975e8-390d-4647-b55d-11d1caf78dcb",
"timestamp": "2026-06-29T17:01:08Z",
"harness": "H6-agentops",
"agent": "demo.agent",
"step": "speckit.implement",
"status": "success",
"exit_code": 0,
"latency_ms": 57,
"retry_count": 0,
"input_tokens": 6,
"output_tokens": 6,
"total_tokens": 2778,
"cost_estimate": 0.08334,
"cost_source": "provider_telemetry",
"hallucination_signals": 0,
"hallucination_matched": [],
"alerts": [],
"input_hash": "2a5a257dc10475c9105ba981755390fb815f3102d3e6d4fc6f7fb161f7351bac",
"output_hash": "2a5a257dc10475c9105ba981755390fb815f3102d3e6d4fc6f7fb161f7351bac",
"error": ""
}
@@ -0,0 +1,22 @@
{
"trace_id": "45331383-aaed-47b7-bfa1-58eba82adcd3",
"timestamp": "2026-06-29T17:01:09Z",
"harness": "H6-agentops",
"agent": "demo.agent",
"step": "failing-step",
"status": "failed",
"exit_code": 7,
"latency_ms": 198,
"retry_count": 0,
"input_tokens": 6,
"output_tokens": 0,
"total_tokens": 6,
"cost_estimate": 0.00001200,
"cost_source": "word_count_estimate",
"hallucination_signals": 0,
"hallucination_matched": [],
"alerts": ["execution-failed"],
"input_hash": "2a5a257dc10475c9105ba981755390fb815f3102d3e6d4fc6f7fb161f7351bac",
"output_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"error": "command exited with code 7"
}
@@ -0,0 +1,22 @@
{
"trace_id": "98e40b76-6db7-4357-83c4-b879308cd645",
"timestamp": "2026-06-29T17:01:08Z",
"harness": "H6-agentops",
"agent": "demo.agent",
"step": "step-1-srs",
"status": "success",
"exit_code": 0,
"latency_ms": 220,
"retry_count": 0,
"input_tokens": 13,
"output_tokens": 13,
"total_tokens": 26,
"cost_estimate": 0.00005200,
"cost_source": "word_count_estimate",
"hallucination_signals": 4,
"hallucination_matched": [{"marker": "I assume", "count": 1}, {"marker": "typically", "count": 1}, {"marker": "I believe", "count": 1}, {"marker": "might be incorrect", "count": 1}],
"alerts": ["hallucination-suspected"],
"input_hash": "666dfe86cafeb8150bcc28d0b5cb2c43e1149dddaa126b5fbc8adee318f46d57",
"output_hash": "666dfe86cafeb8150bcc28d0b5cb2c43e1149dddaa126b5fbc8adee318f46d57",
"error": ""
}
@@ -0,0 +1,22 @@
{
"trace_id": "997d7a8d-7d82-4b85-acd5-7b95ceaa0aaf",
"timestamp": "2026-06-29T17:01:22Z",
"harness": "H6-agentops",
"agent": "unknown-agent",
"step": "unknown-step",
"status": "failed",
"exit_code": 0,
"latency_ms": 210,
"retry_count": 0,
"input_tokens": 3,
"output_tokens": 0,
"total_tokens": 3,
"cost_estimate": 0.00000600,
"cost_source": "word_count_estimate",
"hallucination_signals": 0,
"hallucination_matched": [],
"alerts": ["execution-failed"],
"input_hash": "cae0b3e41bdd7bf9fc5ab7f73d4422a65c3766f8097c90d952d07dd371605290",
"output_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"error": "output file not produced"
}
@@ -0,0 +1,22 @@
{
"trace_id": "a01ce4c3-750c-432f-8580-87486594e061",
"timestamp": "2026-06-29T17:01:07Z",
"harness": "H6-agentops",
"agent": "demo.agent",
"step": "demo-step",
"status": "success",
"exit_code": 0,
"latency_ms": 59,
"retry_count": 0,
"input_tokens": 6,
"output_tokens": 6,
"total_tokens": 12,
"cost_estimate": 0.00002400,
"cost_source": "word_count_estimate",
"hallucination_signals": 0,
"hallucination_matched": [],
"alerts": [],
"input_hash": "2a5a257dc10475c9105ba981755390fb815f3102d3e6d4fc6f7fb161f7351bac",
"output_hash": "2a5a257dc10475c9105ba981755390fb815f3102d3e6d4fc6f7fb161f7351bac",
"error": ""
}
@@ -0,0 +1,22 @@
{
"trace_id": "a32695e8-f4da-4d5e-967d-c5d4b6283995",
"timestamp": "2026-06-29T17:01:10Z",
"harness": "H6-agentops",
"agent": "wrapper.demo",
"step": "wrapper-step",
"status": "success",
"exit_code": 0,
"latency_ms": 58,
"retry_count": 0,
"input_tokens": 7,
"output_tokens": 7,
"total_tokens": 14,
"cost_estimate": 0.00002800,
"cost_source": "word_count_estimate",
"hallucination_signals": 0,
"hallucination_matched": [],
"alerts": [],
"input_hash": "054cd5120725af9fdf9a8033f7b0f60d2d8ba3861926b00686d74700668e5116",
"output_hash": "054cd5120725af9fdf9a8033f7b0f60d2d8ba3861926b00686d74700668e5116",
"error": ""
}
@@ -0,0 +1,22 @@
{
"trace_id": "c54e9272-bc63-4c83-9389-49fc7f8fb312",
"timestamp": "2026-06-29T17:01:12Z",
"harness": "H6-agentops",
"agent": "wrapper.demo",
"step": "wrapper-step",
"status": "success",
"exit_code": 0,
"latency_ms": 219,
"retry_count": 0,
"input_tokens": 7,
"output_tokens": 7,
"total_tokens": 14,
"cost_estimate": 0.00002800,
"cost_source": "word_count_estimate",
"hallucination_signals": 0,
"hallucination_matched": [],
"alerts": [],
"input_hash": "054cd5120725af9fdf9a8033f7b0f60d2d8ba3861926b00686d74700668e5116",
"output_hash": "054cd5120725af9fdf9a8033f7b0f60d2d8ba3861926b00686d74700668e5116",
"error": ""
}
@@ -0,0 +1,22 @@
{
"trace_id": "d5857d18-83ac-4dbf-8d47-aebb4549255d",
"timestamp": "2026-06-29T17:01:23Z",
"harness": "H6-agentops",
"agent": "adv",
"step": "step-1-srs",
"status": "success",
"exit_code": 0,
"latency_ms": 205,
"retry_count": 0,
"input_tokens": 13,
"output_tokens": 13,
"total_tokens": 26,
"cost_estimate": 0.00005200,
"cost_source": "word_count_estimate",
"hallucination_signals": 4,
"hallucination_matched": [{"marker": "I assume", "count": 1}, {"marker": "typically", "count": 1}, {"marker": "I believe", "count": 1}, {"marker": "might be incorrect", "count": 1}],
"alerts": ["hallucination-suspected"],
"input_hash": "666dfe86cafeb8150bcc28d0b5cb2c43e1149dddaa126b5fbc8adee318f46d57",
"output_hash": "666dfe86cafeb8150bcc28d0b5cb2c43e1149dddaa126b5fbc8adee318f46d57",
"error": ""
}
@@ -0,0 +1,22 @@
{
"trace_id": "d5d03ef1-b24b-4168-bae9-27dd16efc987",
"timestamp": "2026-06-29T17:01:24Z",
"harness": "H6-agentops",
"agent": "adv",
"step": "step-1-srs",
"status": "success",
"exit_code": 0,
"latency_ms": 203,
"retry_count": 0,
"input_tokens": 7,
"output_tokens": 7,
"total_tokens": 14,
"cost_estimate": 0.00002800,
"cost_source": "word_count_estimate",
"hallucination_signals": 0,
"hallucination_matched": [],
"alerts": [],
"input_hash": "a97a0aba66ab15562e4d271fbcb2071c92d3662d99f2e2faac7f263ea35cfa0b",
"output_hash": "a97a0aba66ab15562e4d271fbcb2071c92d3662d99f2e2faac7f263ea35cfa0b",
"error": ""
}
@@ -0,0 +1,16 @@
{
"trace_id": "24c9d9fc-916e-4227-87a5-80ac15fe231f",
"timestamp": "2026-06-29T17:01:07Z",
"harness": "H5-governance",
"action": "deploy",
"actor": "developer",
"risk_level": "high",
"decision": "denied",
"approval_status": "approval_required",
"approver": "",
"reasons": ["sensitive-action:deploy", "high-risk-content"],
"input_hash": "3c1cf354bd8e23e98738dd97e726a7c751cf9c7574454d52b9698bfbb9000bff",
"output_hash": "3c1cf354bd8e23e98738dd97e726a7c751cf9c7574454d52b9698bfbb9000bff",
"previous_record_hash": "",
"record_hash": "1fe2871313fb09b9d28d5b60e15da3ed2fc84347b486ff6643b7fa6b859e8f86"
}
@@ -0,0 +1,16 @@
{
"trace_id": "26c1c13f-a278-414f-8f36-1dcaed0f72d3",
"timestamp": "2026-06-29T17:01:21Z",
"harness": "H5-governance",
"action": "write_code",
"actor": "developer",
"risk_level": "medium",
"decision": "approved",
"approval_status": "policy_auto_approved_with_audit",
"approver": "",
"reasons": ["sensitive-action:write_code"],
"input_hash": "d62056ac97257e952127e4ee599f12b633725abe0c5a7c8fbaa82d6c29561190",
"output_hash": "d62056ac97257e952127e4ee599f12b633725abe0c5a7c8fbaa82d6c29561190",
"previous_record_hash": "534b9f4d08ffefe8d22dfad9137d2c739655e2ddb575b1831d0ef06feee3b047",
"record_hash": "779e8b997802a71f7522c8eb434c2f413a826883c6e1b067601a00af83164511"
}
@@ -0,0 +1,16 @@
{
"trace_id": "68de2633-d730-423c-99b1-189c2b3bea55",
"timestamp": "2026-06-29T17:01:22Z",
"harness": "H5-governance",
"action": "write_code",
"actor": "developer",
"risk_level": "medium",
"decision": "approved",
"approval_status": "policy_auto_approved_with_audit",
"approver": "",
"reasons": ["sensitive-action:write_code"],
"input_hash": "d62056ac97257e952127e4ee599f12b633725abe0c5a7c8fbaa82d6c29561190",
"output_hash": "d62056ac97257e952127e4ee599f12b633725abe0c5a7c8fbaa82d6c29561190",
"previous_record_hash": "779e8b997802a71f7522c8eb434c2f413a826883c6e1b067601a00af83164511",
"record_hash": "174f322c1957a0d313ac09b8d555248fd9ddb208fe813451cd565359660d5df3"
}
@@ -0,0 +1,16 @@
{
"trace_id": "73438130-f67c-4279-94c2-8a9e780691e8",
"timestamp": "2026-06-29T17:01:11Z",
"harness": "H5-governance",
"action": "agent_step",
"actor": "developer",
"risk_level": "low",
"decision": "approved",
"approval_status": "auto_approved",
"approver": "",
"reasons": [],
"input_hash": "0649852ef55d103b9b1134daf5324e92a3e2254386dc137787ca9432b2e4b9bf",
"output_hash": "0649852ef55d103b9b1134daf5324e92a3e2254386dc137787ca9432b2e4b9bf",
"previous_record_hash": "b411874dd4146d21a63c4140e1e08110ba398d11fff170472b20198867e89133",
"record_hash": "8615f2bd2899912d2804fe93a4fe68b14471fd339a3ade74aef9720f8d031906"
}
@@ -0,0 +1,16 @@
{
"trace_id": "8bb3e146-0e09-4abe-b45c-efe65f38404f",
"timestamp": "2026-06-29T17:01:19Z",
"harness": "H5-governance",
"action": "deploy",
"actor": "alice",
"risk_level": "high",
"decision": "denied",
"approval_status": "separation_of_duties_violation",
"approver": "alice",
"reasons": ["sensitive-action:deploy", "high-risk-content", "separation-of-duties:actor-equals-approver"],
"input_hash": "ec9c2145d75be1bf0080711b5bd106465a8307147205ea249bfa1faa0bf05bbb",
"output_hash": "ec9c2145d75be1bf0080711b5bd106465a8307147205ea249bfa1faa0bf05bbb",
"previous_record_hash": "8615f2bd2899912d2804fe93a4fe68b14471fd339a3ade74aef9720f8d031906",
"record_hash": "f3b567697e9169fe3f3d7d314b49d1a1e7f7b56f2ef3c7e375ad2be5bf2f8d0e"
}
@@ -0,0 +1,16 @@
{
"trace_id": "ac7cf912-c123-4c02-94ad-5fd14bd8e5d6",
"timestamp": "2026-06-29T17:01:19Z",
"harness": "H5-governance",
"action": "deploy",
"actor": "alice",
"risk_level": "high",
"decision": "approved",
"approval_status": "human_approved",
"approver": "bob",
"reasons": ["sensitive-action:deploy", "high-risk-content"],
"input_hash": "ec9c2145d75be1bf0080711b5bd106465a8307147205ea249bfa1faa0bf05bbb",
"output_hash": "ec9c2145d75be1bf0080711b5bd106465a8307147205ea249bfa1faa0bf05bbb",
"previous_record_hash": "f3b567697e9169fe3f3d7d314b49d1a1e7f7b56f2ef3c7e375ad2be5bf2f8d0e",
"record_hash": "534b9f4d08ffefe8d22dfad9137d2c739655e2ddb575b1831d0ef06feee3b047"
}
@@ -0,0 +1,16 @@
{
"trace_id": "f8816605-85f8-4d98-9616-b32c1eb93b10",
"timestamp": "2026-06-29T17:01:07Z",
"harness": "H5-governance",
"action": "deploy",
"actor": "developer",
"risk_level": "high",
"decision": "approved",
"approval_status": "human_approved",
"approver": "architect@example.local",
"reasons": ["sensitive-action:deploy", "high-risk-content"],
"input_hash": "3c1cf354bd8e23e98738dd97e726a7c751cf9c7574454d52b9698bfbb9000bff",
"output_hash": "3c1cf354bd8e23e98738dd97e726a7c751cf9c7574454d52b9698bfbb9000bff",
"previous_record_hash": "1fe2871313fb09b9d28d5b60e15da3ed2fc84347b486ff6643b7fa6b859e8f86",
"record_hash": "0d922f790616f1cd2a33cbfd21a042ebbc25532ff7b9785642a145bd298cd60a"
}
@@ -0,0 +1,16 @@
{
"trace_id": "fd1f03a5-d72a-4a54-a376-3f183bb1d47a",
"timestamp": "2026-06-29T17:01:10Z",
"harness": "H5-governance",
"action": "agent_step",
"actor": "developer",
"risk_level": "low",
"decision": "approved",
"approval_status": "auto_approved",
"approver": "",
"reasons": [],
"input_hash": "0649852ef55d103b9b1134daf5324e92a3e2254386dc137787ca9432b2e4b9bf",
"output_hash": "0649852ef55d103b9b1134daf5324e92a3e2254386dc137787ca9432b2e4b9bf",
"previous_record_hash": "0d922f790616f1cd2a33cbfd21a042ebbc25532ff7b9785642a145bd298cd60a",
"record_hash": "b411874dd4146d21a63c4140e1e08110ba398d11fff170472b20198867e89133"
}
@@ -0,0 +1,12 @@
{
"trace_id": "0a1578fd-5e42-4796-9313-c00fd6936f2f",
"timestamp": "2026-06-29T17:01:18Z",
"harness": "H4-security",
"mode": "input",
"status": "blocked",
"action": "block",
"risk_level": "high",
"matched_rules": ["prompt-injection:(postgres|mysql|mongodb)://[^@]+@", "secret-in-input"],
"input_hash": "ad1d539f1df96460a8045da532dc6b4782f117a1a131c15d986abfc95db583eb",
"output_hash": "0c1bde32600f6661e10e21b6da145740b31d9a380d6324e9831a24540864a479"
}
@@ -0,0 +1,12 @@
{
"trace_id": "0d29681f-4fc6-4e89-8fab-98a6e560a9f8",
"timestamp": "2026-06-29T17:01:05Z",
"harness": "H4-security",
"mode": "input",
"status": "pass",
"action": "allow",
"risk_level": "low",
"matched_rules": [],
"input_hash": "c9e8e44c7c4b03acf3a39c0343d25c371d54668be1015cd743d5c289c62ac587",
"output_hash": "ae9d8e18b2c01ad609f3a9e8e36072c116ea6f088c8266a1b56323418c3b7066"
}
@@ -0,0 +1,12 @@
{
"trace_id": "14027a15-8de9-4f1e-bf3b-d7bace5feffb",
"timestamp": "2026-06-29T17:01:23Z",
"harness": "H4-security",
"mode": "output",
"status": "pass",
"action": "allow",
"risk_level": "low",
"matched_rules": [],
"input_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"output_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
}
@@ -0,0 +1,12 @@
{
"trace_id": "1a060656-550a-4cc3-8190-6b74d02cfc66",
"timestamp": "2026-06-29T17:01:15Z",
"harness": "H4-security",
"mode": "input",
"status": "blocked",
"action": "block",
"risk_level": "high",
"matched_rules": ["prompt-injection:ignore (all |any )?(previous|prior|above|the above|earlier) (instruction|instructions|rule|rules|prompt|prompts|guideline|guidelines)", "prompt-injection:ignore (all )?(previous|prior|above) (instructions|guidelines|rules)"],
"input_hash": "f71ca2245e40db381bc39caa0264e22f222758a67683c5b82721b921f91a38cc",
"output_hash": "f71ca2245e40db381bc39caa0264e22f222758a67683c5b82721b921f91a38cc"
}
@@ -0,0 +1,12 @@
{
"trace_id": "1cca9feb-d7b3-4e87-89a4-5aaafa519981",
"timestamp": "2026-06-29T17:01:21Z",
"harness": "H4-security",
"mode": "input",
"status": "pass",
"action": "allow",
"risk_level": "low",
"matched_rules": [],
"input_hash": "d62056ac97257e952127e4ee599f12b633725abe0c5a7c8fbaa82d6c29561190",
"output_hash": "d62056ac97257e952127e4ee599f12b633725abe0c5a7c8fbaa82d6c29561190"
}
@@ -0,0 +1,12 @@
{
"trace_id": "3e4dd63e-1d00-4043-9fb4-13cd677adfd8",
"timestamp": "2026-06-29T17:01:20Z",
"harness": "H4-security",
"mode": "input",
"status": "pass",
"action": "allow",
"risk_level": "low",
"matched_rules": [],
"input_hash": "d62056ac97257e952127e4ee599f12b633725abe0c5a7c8fbaa82d6c29561190",
"output_hash": "d62056ac97257e952127e4ee599f12b633725abe0c5a7c8fbaa82d6c29561190"
}
@@ -0,0 +1,12 @@
{
"trace_id": "52d06637-5282-4d6f-8757-7ab7e0e0d69a",
"timestamp": "2026-06-29T17:01:16Z",
"harness": "H4-security",
"mode": "input",
"status": "blocked",
"action": "block",
"risk_level": "high",
"matched_rules": ["prompt-injection:forget (all |everything |the |your )?.*(instruction|rule|previous|prior|above)", "prompt-injection:reveal (the )?(system|hidden|internal|original) (prompt|instruction|instructions)"],
"input_hash": "eee04d186d963d68cbe0bf7a7bbb5098bbc51b3489fe0c322ebac58380475cdc",
"output_hash": "eee04d186d963d68cbe0bf7a7bbb5098bbc51b3489fe0c322ebac58380475cdc"
}
@@ -0,0 +1,12 @@
{
"trace_id": "5f48b3bd-0ac6-4b39-b65a-1e44455ab65c",
"timestamp": "2026-06-29T17:01:09Z",
"harness": "H4-security",
"mode": "input",
"status": "pass",
"action": "allow",
"risk_level": "low",
"matched_rules": [],
"input_hash": "267c99d13eb60ecfc59c51625918582143c1e580f32d1181985058d53cd8e4a2",
"output_hash": "0649852ef55d103b9b1134daf5324e92a3e2254386dc137787ca9432b2e4b9bf"
}
@@ -0,0 +1,12 @@
{
"trace_id": "7b128544-88d6-4f3f-b89e-567a80c277da",
"timestamp": "2026-06-29T17:01:11Z",
"harness": "H4-security",
"mode": "input",
"status": "pass",
"action": "allow",
"risk_level": "low",
"matched_rules": [],
"input_hash": "267c99d13eb60ecfc59c51625918582143c1e580f32d1181985058d53cd8e4a2",
"output_hash": "0649852ef55d103b9b1134daf5324e92a3e2254386dc137787ca9432b2e4b9bf"
}
@@ -0,0 +1,12 @@
{
"trace_id": "7d738dd4-0c71-4ee2-88be-314db2d2793d",
"timestamp": "2026-06-29T17:01:15Z",
"harness": "H4-security",
"mode": "input",
"status": "blocked",
"action": "block",
"risk_level": "high",
"matched_rules": ["prompt-injection:ignore (all |any )?(previous|prior|above|the above|earlier) (instruction|instructions|rule|rules|prompt|prompts|guideline|guidelines)", "prompt-injection:you are now", "prompt-injection:(ignore previous instruction|bypass system|override policy)", "prompt-injection:(you are now|act as admin|pretend to be system)", "prompt-injection:ignore (all )?(previous|prior|above) (instructions|guidelines|rules)"],
"input_hash": "319e57557373ad701207bafb3ce13d3578fd16af6ce3ee0cc71ad35d35a4c156",
"output_hash": "319e57557373ad701207bafb3ce13d3578fd16af6ce3ee0cc71ad35d35a4c156"
}
@@ -0,0 +1,12 @@
{
"trace_id": "8f5f5208-a8cd-43b0-930c-bdcf169a4ccd",
"timestamp": "2026-06-29T17:01:05Z",
"harness": "H4-security",
"mode": "input",
"status": "blocked",
"action": "block",
"risk_level": "high",
"matched_rules": ["prompt-injection:ignore (all |any )?(previous|prior|above|the above|earlier) (instruction|instructions|rule|rules|prompt|prompts|guideline|guidelines)", "prompt-injection:reveal (the )?(system|hidden|internal|original) (prompt|instruction|instructions)", "prompt-injection:(ignore previous instruction|bypass system|override policy)", "prompt-injection:ignore (all )?(previous|prior|above) (instructions|guidelines|rules)"],
"input_hash": "035b92ef321008bd61f79796ffc237b8476e2e008b8f2ee2c5b6cb781bd7d69c",
"output_hash": "035b92ef321008bd61f79796ffc237b8476e2e008b8f2ee2c5b6cb781bd7d69c"
}
@@ -0,0 +1,12 @@
{
"trace_id": "a4009f85-52af-41e6-85d3-7c904bd46115",
"timestamp": "2026-06-29T17:01:17Z",
"harness": "H4-security",
"mode": "input",
"status": "blocked",
"action": "block",
"risk_level": "high",
"matched_rules": ["prompt-injection:ignore (all |any )?(previous|prior|above|the above|earlier) (instruction|instructions|rule|rules|prompt|prompts|guideline|guidelines)", "prompt-injection:(ignore previous instruction|bypass system|override policy)", "prompt-injection:ignore (all )?(previous|prior|above) (instructions|guidelines|rules)"],
"input_hash": "9b3b6f8d8e3bb7db8b4039996e14c04d9ac3453f1968934975c7fb33be3b4470",
"output_hash": "9b3b6f8d8e3bb7db8b4039996e14c04d9ac3453f1968934975c7fb33be3b4470"
}
@@ -0,0 +1,12 @@
{
"trace_id": "a62b63a5-0ba1-4617-bc55-fd462234b43a",
"timestamp": "2026-06-29T17:01:06Z",
"harness": "H4-security",
"mode": "input",
"status": "blocked",
"action": "block",
"risk_level": "high",
"matched_rules": ["prompt-injection:-----BEGIN (RSA |EC )?PRIVATE KEY-----", "secret-in-input"],
"input_hash": "1bb7c8b981b9830a7b0098d5a4cb2283ef2eda3ee31926f4de50db96fba1e585",
"output_hash": "06be335c7385a9ae5cbc07c8112991635742098ff4418756637ed598b98ddb92"
}
@@ -0,0 +1,12 @@
{
"trace_id": "b1269a1e-bfe4-414e-92c5-42c546f93874",
"timestamp": "2026-06-29T17:01:11Z",
"harness": "H4-security",
"mode": "output",
"status": "pass",
"action": "allow",
"risk_level": "low",
"matched_rules": [],
"input_hash": "0649852ef55d103b9b1134daf5324e92a3e2254386dc137787ca9432b2e4b9bf",
"output_hash": "0649852ef55d103b9b1134daf5324e92a3e2254386dc137787ca9432b2e4b9bf"
}
@@ -0,0 +1,12 @@
{
"trace_id": "b7196c7d-3cec-4a33-80dc-6df08709e845",
"timestamp": "2026-06-29T17:01:16Z",
"harness": "H4-security",
"mode": "input",
"status": "blocked",
"action": "block",
"risk_level": "high",
"matched_rules": ["prompt-injection:disregard (all |any |the )?.*(rule|instruction|previous|prior|above|earlier|policy|guideline)", "prompt-injection:act without (any )?(restriction|restrictions|rule|rules|limit|limits|filter|guardrail)"],
"input_hash": "0ecb593740a007ca236c2bdfa0f5fd8f238a8e90576f0973a3f131667244b32d",
"output_hash": "0ecb593740a007ca236c2bdfa0f5fd8f238a8e90576f0973a3f131667244b32d"
}
@@ -0,0 +1,12 @@
{
"trace_id": "b9aeddaf-d337-4e65-bd60-535d4e27ac91",
"timestamp": "2026-06-29T17:01:17Z",
"harness": "H4-security",
"mode": "input",
"status": "blocked",
"action": "block",
"risk_level": "high",
"matched_rules": ["prompt-injection:-----BEGIN (RSA |EC )?PRIVATE KEY-----", "secret-in-input"],
"input_hash": "d74b29905bc2219494c55c82b3f0c0e24eee5ea8c5c0279e5b58c4aab57bf8da",
"output_hash": "1318f233f3d8afdd23b9dbe5eb7a338758c39c5b9e86cc0a653779858ed60585"
}
@@ -0,0 +1,12 @@
{
"trace_id": "bcedbd04-ffb8-49ea-b2aa-d1839a0072a9",
"timestamp": "2026-06-29T17:01:12Z",
"harness": "H4-security",
"mode": "output",
"status": "pass",
"action": "allow",
"risk_level": "low",
"matched_rules": [],
"input_hash": "0649852ef55d103b9b1134daf5324e92a3e2254386dc137787ca9432b2e4b9bf",
"output_hash": "0649852ef55d103b9b1134daf5324e92a3e2254386dc137787ca9432b2e4b9bf"
}
@@ -0,0 +1,12 @@
{
"trace_id": "c60dc00a-9615-44cc-8c2e-4b4196201739",
"timestamp": "2026-06-29T17:01:06Z",
"harness": "H4-security",
"mode": "input",
"status": "blocked",
"action": "block",
"risk_level": "high",
"matched_rules": ["prompt-injection:pretend (to be|you are|you're) (system|not|no longer|an unrestricted)", "prompt-injection:developer mode", "prompt-injection:pretend (you are|you're) (not|no longer) (an AI|Claude|an assistant)", "prompt-injection:(developer mode|DAN mode|jailbreak mode|unrestricted mode)"],
"input_hash": "ccae83f7a01fac243ce8287d541c19b19505a1dc1cbb115319788ee4e67bc7aa",
"output_hash": "ccae83f7a01fac243ce8287d541c19b19505a1dc1cbb115319788ee4e67bc7aa"
}
@@ -0,0 +1,12 @@
{
"trace_id": "db192f2a-28e9-4009-b529-de5d74029af9",
"timestamp": "2026-06-29T17:01:18Z",
"harness": "H4-security",
"mode": "output",
"status": "blocked",
"action": "block",
"risk_level": "high",
"matched_rules": ["secret-in-output"],
"input_hash": "0e3b85a844f6c5f282d095866d9811dd8d14048fc0f2669a4c3fb19a91d6c7eb",
"output_hash": "095b06c8bd180255e390c3061508d5b4cfd88527edb7f022952510c3777d2bbd"
}
@@ -0,0 +1,12 @@
{
"trace_id": "f0019d48-a289-4493-be90-7bd24115796e",
"timestamp": "2026-06-29T17:01:18Z",
"harness": "H4-security",
"mode": "input",
"status": "pass",
"action": "allow",
"risk_level": "low",
"matched_rules": [],
"input_hash": "11f1d3168daa61cfb1195bb8aed22b922bd13ccbbdeeec6cc5512b46e10ffedb",
"output_hash": "11f1d3168daa61cfb1195bb8aed22b922bd13ccbbdeeec6cc5512b46e10ffedb"
}
@@ -0,0 +1,92 @@
<!--
Sync Impact Report
Version change: template -> 1.0.0
Modified principles: placeholder principle 1 -> I. Requirement-First Definition; placeholder principle 2 -> II. Workflow and Role Traceability; placeholder principle 3 -> III. Test-First Delivery (Non-Negotiable); placeholder principle 4 -> IV. Reviewable Independent Increments; placeholder principle 5 -> V. Minimal Technical Commitment and Simplicity
Added sections: Product Scope Constraints; Delivery Workflow and Quality Gates
Removed sections: none
Templates requiring updates:
- ✅ .specify/templates/spec-template.md
- ✅ .specify/templates/plan-template.md
- ✅ .specify/templates/tasks-template.md
- ⚠ pending none in .specify/templates/commands because that directory does not exist in this repository
- ✅ README.md
Follow-up TODOs: none
-->
# OKR Web AI-SDLC Constitution
## Core Principles
### I. Requirement-First Definition
Every feature artifact MUST begin from validated business intent, user outcomes, and workflow purpose.
Specifications MUST describe what the system does and why the behavior matters before any implementation
approach is chosen. If the input does not mandate a technical decision, the specification MUST leave that
decision open and record the unresolved point explicitly instead of inventing detail.
Rationale: This project exists to transform customer requirements into reviewable delivery artifacts. Early
technical commitments create rework and hide requirement gaps.
### II. Workflow and Role Traceability
Each feature MUST trace back to a user role, a business workflow step, and an observable business outcome.
Specifications, plans, and tasks MUST preserve that traceability so reviewers can verify why each unit of work
exists. Scope that appears in mockups or examples but lacks defined behavior MUST be marked as excluded or
clarification-needed until the requirement defines it.
Rationale: The OKR system depends on role-driven behavior across employee, manager, and admin workflows.
Traceability prevents hidden assumptions and keeps delivery aligned with the stated process.
### III. Test-First Delivery (Non-Negotiable)
Strict TDD is mandatory. For every behavior being implemented, tests MUST be written first, MUST be observed
failing for the intended reason, and only then may production code be changed. Red-green-refactor is the
required delivery cycle. No feature, fix, or refactor is complete unless automated tests prove the behavior.
Rationale: This repository uses AI-assisted delivery. Test-first execution is the control mechanism that keeps
generated or assisted code aligned with the requirement and safe to evolve.
### IV. Reviewable Independent Increments
User stories MUST be written and planned as independently testable slices that deliver visible value on their
own. Each story MUST have acceptance scenarios, explicit success criteria, and a clear independent test path.
Plans and tasks MUST allow reviewers to validate one increment without requiring the entire product to be done.
Rationale: Small, reviewable increments reduce ambiguity, surface defects earlier, and fit the project's staged
AI-SDLC workflow.
### V. Minimal Technical Commitment and Simplicity
Artifacts created before planning MUST avoid unnecessary architecture, tooling, or implementation detail.
Technical choices MAY be documented only when the input explicitly mandates them or when deferring the choice
would block requirement understanding. When a decision is necessary, the simplest option that satisfies the
requirement and preserves future change MUST be preferred.
Rationale: Simplicity keeps the specification readable and keeps later planning honest about what is known
versus what is still design work.
## Product Scope Constraints
This constitution governs an internal web-based OKR management system used in a workshop context. Current
requirement scope includes authentication, objective creation, key result definition, manager approval,
progress tracking, end-of-cycle evaluation, and dashboard visibility. The system MUST support company,
department, and individual OKR management concepts where they are defined by the requirement. Production-scale
operational concerns MUST not be assumed unless they are explicitly mandated by the input artifact for the
feature being specified or planned.
## Delivery Workflow and Quality Gates
Work MUST proceed in the following order: requirement input, specification, clarification of gaps, plan,
design artifacts, tasks, implementation, review, and test execution. A specification is not ready for planning
unless it is clear, reviewable, technology-agnostic except for mandated constraints, and contains explicit
clarification items for unresolved business rules. A plan is not ready for implementation unless its
constitution check confirms strict TDD, independent story delivery, and traceability back to the specification.
Tasks MUST list test work before implementation work for every user story. Reviews MUST reject artifacts that
replace missing requirements with undocumented assumptions.
## Governance
This constitution overrides conflicting local habits and template defaults. Amendments MUST document the reason
for change, the affected principles or sections, the semantic version bump rationale, and any downstream
template updates required to keep the workflow aligned. Versioning follows semantic rules: MAJOR for breaking
governance changes or removal of principles, MINOR for new principles or materially expanded obligations, and
PATCH for clarifications that do not change expected behavior. Compliance review is mandatory during spec,
plan, task, and implementation review. Any exception MUST be documented in the relevant artifact with explicit
justification and reviewer acknowledgement.
**Version**: 1.0.0 | **Ratified**: 2026-04-05 | **Last Amended**: 2026-04-05
@@ -0,0 +1,136 @@
# 🔐 Security Policy
## Project: AINATIVE_OKR_CLAUDE_GHCP
---
# 1. Purpose
Tài liệu này định nghĩa chính sách bảo mật cho AI System nhằm:
- Ngăn chặn prompt injection
- Bảo vệ dữ liệu nhạy cảm (PII, secret)
- Kiểm soát hành vi của LLM/Agent
- Đảm bảo an toàn khi vận hành production
---
# 2. Scope
Áp dụng cho toàn bộ hệ thống:
- Prompt / LLM interaction
- Agent workflow
- Template generation (SpecKit)
- Script automation
- Logging & monitoring
---
# 3. Security Principles
- ✅ **Zero Trust Input**: Không tin bất kỳ input nào từ user
- ✅ **Least Privilege**: Tool/Agent chỉ có quyền tối thiểu
- ✅ **Defense in Depth**: nhiều lớp bảo vệ
- ✅ **Auditability**: mọi hoạt động phải log được
- ✅ **Fail Safe**: khi nghi ngờ → block hoặc require approval
---
# 4. Threat Model (OWASP LLM Top 10 based)
| Threat | Description |
|------|-------------|
| Prompt Injection | User cố override system |
| Data Leakage | Lộ thông tin nội bộ |
| Secret Exposure | Lộ API key / credential |
| Unsafe Tool Call | Agent thực thi hành động nguy hiểm |
| Hallucination Risk | Output sai nhưng có vẻ đúng |
---
# 5. Input Security Policy
## 5.1 Prompt Injection Protection
Block các pattern:
- "ignore previous instruction"
- "bypass system"
- "override policy"
### Action
| Level | Action |
|------|--------|
| High | Block |
| Medium | Require Approval |
| Low | Log |
---
## 5.2 PII Detection
Detect:
- Email
- Phone number
- Personal ID
- Address
### Action
- Mask trước khi gửi LLM
- Log sự kiện
---
## 5.3 Secret Detection
Detect:
- API Keys
- Tokens
- Password
### Action
- Block
- Alert system
- Không gửi vào LLM
---
# 6. Output Security Policy
## 6.1 Data Leakage Prevention
- Không output:
- nội dung confidential
- internal system prompt
- hidden instruction
## 6.2 Output Filtering
| Type | Action |
|------|--------|
| PII | Mask |
| Secret | Remove |
| Unsafe content | Block |
| Hallucination risk | Flag |
---
# 7. Tool Security Policy
## 7.1 Tool Access Control
| Tool | Permission |
|------|----------|
| Read File | Allow |
| Write File | Restricted |
| Delete File | Require Approval |
| External API | Whitelist only |
---
## 7.2 Safe Tool Execution Flow
+208
View File
@@ -0,0 +1,208 @@
#!/usr/bin/env bash
set -euo pipefail
# CASAN H6 AgentOps Harness
# Usage:
# agent-metrics.sh <input-file> <output-file> [-- <command> ...]
#
# If command is omitted, the script performs a pass-through copy. If command is
# provided, it runs with CASAN_INPUT and CASAN_OUTPUT environment variables.
INPUT_FILE="${1:-}"
OUTPUT_FILE="${2:-}"
shift 2 || true
if [[ "${1:-}" == "--" ]]; then
shift
fi
if [[ -z "$INPUT_FILE" || -z "$OUTPUT_FILE" ]]; then
echo "Usage: agent-metrics.sh <input-file> <output-file> [-- <command> ...]" >&2
exit 64
fi
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
LOG_DIR="$PROJECT_ROOT/.specify/logs"
TRACE_DIR="$LOG_DIR/trace"
METRICS_DIR="$LOG_DIR/cost"
ALERT_LOG="$PROJECT_ROOT/.specify/agentops/alerts.log"
METRICS_LOG="$METRICS_DIR/metrics.jsonl"
mkdir -p "$TRACE_DIR" "$METRICS_DIR" "$(dirname "$OUTPUT_FILE")" "$(dirname "$ALERT_LOG")"
# shellcheck source=tool-audit-lib.sh
source "$SCRIPT_DIR/tool-audit-lib.sh"
if [[ ! -f "$INPUT_FILE" ]]; then
echo "AGENTOPS_FAILED: input file not found: $INPUT_FILE" >&2
exit 1
fi
timestamp() {
date -u +"%Y-%m-%dT%H:%M:%SZ"
}
epoch_ms() {
python3 -c 'import time; print(int(time.time() * 1000))' 2>/dev/null || printf '%s000\n' "$(date +%s)"
}
new_trace_id() {
if command -v uuidgen >/dev/null 2>&1; then
uuidgen | tr '[:upper:]' '[:lower:]'
else
printf 'trace-%s-%s\n' "$(date +%s)" "$$"
fi
}
hash_text() {
if command -v sha256sum >/dev/null 2>&1; then
sha256sum | awk '{print $1}'
else
shasum -a 256 | awk '{print $1}'
fi
}
word_count() {
wc -w < "$1" | tr -d ' '
}
TRACE_ID="$(new_trace_id)"
START_TS="$(timestamp)"
START_MS="$(epoch_ms)"
STATUS="success"
ERROR_MSG=""
EXIT_CODE=0
RETRY_COUNT="${CASAN_RETRY_COUNT:-0}"
AGENT_NAME="${CASAN_AGENT_NAME:-unknown-agent}"
STEP_NAME="${CASAN_STEP_NAME:-unknown-step}"
INPUT_TOKENS="$(word_count "$INPUT_FILE")"
if [[ "$#" -gt 0 ]]; then
set +e
CASAN_INPUT="$INPUT_FILE" CASAN_OUTPUT="$OUTPUT_FILE" "$@"
EXIT_CODE=$?
set -e
if [[ "$EXIT_CODE" -ne 0 ]]; then
STATUS="failed"
ERROR_MSG="command exited with code $EXIT_CODE"
fi
TOOL_AUDIT_RECORD="$(python3 - "$START_TS" "$TRACE_ID" "$AGENT_NAME" "$STEP_NAME" "$*" "$EXIT_CODE" "$STATUS" <<'PY'
import json, sys
ts, trace, agent, step, cmd, code, status = sys.argv[1:]
print(json.dumps({
"timestamp": ts, "trace_id": trace, "agent": agent, "step": step,
"tool": "Bash", "command": cmd, "exit_code": int(code), "status": status,
}))
PY
)"
append_tool_audit "$TOOL_AUDIT_RECORD" "$PROJECT_ROOT"
else
cp "$INPUT_FILE" "$OUTPUT_FILE"
fi
END_MS="$(epoch_ms)"
LATENCY_MS=$((END_MS - START_MS))
if [[ ! -f "$OUTPUT_FILE" ]]; then
STATUS="failed"
ERROR_MSG="${ERROR_MSG:-output file not produced}"
: > "$OUTPUT_FILE"
fi
OUTPUT_TOKENS="$(word_count "$OUTPUT_FILE")"
TOTAL_TOKENS=$((INPUT_TOKENS + OUTPUT_TOKENS))
COST_PER_1K="${CASAN_COST_PER_1K:-0.002}"
COST_ESTIMATE="$(python3 - "$TOTAL_TOKENS" "$COST_PER_1K" <<'PY'
import sys
tokens = int(sys.argv[1])
rate = float(sys.argv[2])
print(f"{tokens * rate / 1000:.8f}")
PY
)"
COST_SOURCE="word_count_estimate"
# Prefer real provider usage when telemetry has been imported; the word-count
# figure above is an explicit fallback, not presented as a real billed cost.
PROVIDER_LOG="$PROJECT_ROOT/.specify/logs/level5/provider-usage.jsonl"
if [[ -f "$PROVIDER_LOG" ]] && command -v python3 >/dev/null 2>&1; then
# Use real provider telemetry ONLY when a record genuinely matches this step.
# Do NOT fall back to an arbitrary record (that would reuse one sample's cost
# across every step and misrepresent it as real per-step billing).
PROV="$(python3 "$SCRIPT_DIR/provider-cost-lookup.py" "$PROVIDER_LOG" "$STEP_NAME")"
if [[ -n "$PROV" ]]; then
TOTAL_TOKENS="${PROV%% *}"
COST_ESTIMATE="${PROV##* }"
COST_SOURCE="provider_telemetry"
fi
fi
# Real hallucination-signal detection (populates hallucination-tracking.yaml's metric).
HALLU_YAML="$PROJECT_ROOT/.specify/agentops/hallucination-tracking.yaml"
HALLUCINATION_SIGNALS=0
HALLUCINATION_MATCHED="[]"
if command -v python3 >/dev/null 2>&1; then
HSCAN="$(python3 "$SCRIPT_DIR/hallucination-scan.py" "$HALLU_YAML" "$OUTPUT_FILE" 2>/dev/null || printf '0\n[]')"
HALLUCINATION_SIGNALS="$(printf '%s' "$HSCAN" | head -1)"
HALLUCINATION_MATCHED="$(printf '%s' "$HSCAN" | tail -1)"
fi
INPUT_HASH="$(cat "$INPUT_FILE" | hash_text)"
OUTPUT_HASH="$(cat "$OUTPUT_FILE" | hash_text)"
ALERTS=()
if [[ "$LATENCY_MS" -gt "${CASAN_LATENCY_ALERT_MS:-5000}" ]]; then
ALERTS+=("high-latency")
fi
if [[ "$RETRY_COUNT" -gt "${CASAN_RETRY_ALERT_THRESHOLD:-2}" ]]; then
ALERTS+=("high-retry")
fi
if [[ "$STATUS" == "failed" ]]; then
ALERTS+=("execution-failed")
fi
if [[ "$TOTAL_TOKENS" -gt "${CASAN_TOKEN_ALERT_THRESHOLD:-5000}" ]]; then
ALERTS+=("token-overuse")
fi
if [[ "$HALLUCINATION_SIGNALS" -ge "${CASAN_HALLUCINATION_WARN:-3}" ]]; then
ALERTS+=("hallucination-suspected")
fi
ALERTS_JSON="$(printf '%s\n' "${ALERTS[@]:-}" | python3 -c 'import json,sys; print(json.dumps([x for x in sys.stdin.read().splitlines() if x]))')"
TRACE_FILE="$TRACE_DIR/agentops-$TRACE_ID.json"
cat > "$TRACE_FILE" <<EOF
{
"trace_id": "$TRACE_ID",
"timestamp": "$START_TS",
"harness": "H6-agentops",
"agent": "$AGENT_NAME",
"step": "$STEP_NAME",
"status": "$STATUS",
"exit_code": $EXIT_CODE,
"latency_ms": $LATENCY_MS,
"retry_count": $RETRY_COUNT,
"input_tokens": $INPUT_TOKENS,
"output_tokens": $OUTPUT_TOKENS,
"total_tokens": $TOTAL_TOKENS,
"cost_estimate": $COST_ESTIMATE,
"cost_source": "$COST_SOURCE",
"hallucination_signals": $HALLUCINATION_SIGNALS,
"hallucination_matched": $HALLUCINATION_MATCHED,
"alerts": $ALERTS_JSON,
"input_hash": "$INPUT_HASH",
"output_hash": "$OUTPUT_HASH",
"error": "$ERROR_MSG"
}
EOF
printf '{"timestamp":"%s","trace_id":"%s","harness":"H6-agentops","agent":"%s","step":"%s","status":"%s","exit_code":%s,"latency_ms":%s,"retry_count":%s,"input_tokens":%s,"output_tokens":%s,"total_tokens":%s,"cost_estimate":%s,"cost_source":"%s","hallucination_signals":%s,"alerts":%s,"input_hash":"%s","output_hash":"%s"}\n' \
"$START_TS" "$TRACE_ID" "$AGENT_NAME" "$STEP_NAME" "$STATUS" "$EXIT_CODE" "$LATENCY_MS" "$RETRY_COUNT" "$INPUT_TOKENS" "$OUTPUT_TOKENS" "$TOTAL_TOKENS" "$COST_ESTIMATE" "$COST_SOURCE" "$HALLUCINATION_SIGNALS" "$ALERTS_JSON" "$INPUT_HASH" "$OUTPUT_HASH" >> "$METRICS_LOG"
for alert in "${ALERTS[@]:-}"; do
if [[ -n "$alert" ]]; then
printf '{"timestamp":"%s","trace_id":"%s","severity":"WARN","resource":{"service.name":"%s","service.version":"1.0.0"},"body":{"message":"Alert triggered: %s","alert.type":"%s","step.name":"%s"},"attributes":{"latency_ms":%s,"status":"%s"}}\n' \
"$START_TS" "$TRACE_ID" "$AGENT_NAME" "$alert" "$alert" "$STEP_NAME" "$LATENCY_MS" "$STATUS" >> "$ALERT_LOG"
fi
done
echo "AGENTOPS_RECORDED trace_id=$TRACE_ID status=$STATUS latency_ms=$LATENCY_MS tokens=$TOTAL_TOKENS cost=$COST_ESTIMATE output=$OUTPUT_FILE"
exit "$EXIT_CODE"
@@ -0,0 +1,53 @@
#!/usr/bin/env bash
set -euo pipefail
# CASAN Level 5 business KPI feedback report.
# Usage:
# business-kpi-report.sh <input-json> <output-json>
INPUT_JSON="${1:-}"
OUTPUT_JSON="${2:-}"
if [[ -z "$INPUT_JSON" || -z "$OUTPUT_JSON" ]]; then
echo "Usage: business-kpi-report.sh <input-json> <output-json>" >&2
exit 64
fi
mkdir -p "$(dirname "$OUTPUT_JSON")"
python3 - "$INPUT_JSON" "$OUTPUT_JSON" <<'PY'
import json
import sys
from datetime import datetime, timezone
input_path, output_path = sys.argv[1], sys.argv[2]
data = json.load(open(input_path, encoding="utf-8"))
results = []
for item in data["kpis"]:
baseline = float(item["baseline"])
current = float(item["current"])
target = float(item["target"])
direction = item.get("direction", "lower_is_better")
if direction == "lower_is_better":
improvement = (baseline - current) / baseline if baseline else 0
target_met = current <= target
else:
improvement = (current - baseline) / baseline if baseline else 0
target_met = current >= target
results.append({
"id": item["id"],
"baseline": baseline,
"current": current,
"target": target,
"improvement_ratio": round(improvement, 4),
"target_met": target_met,
})
report = {
"timestamp": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
"harness": "L5-business-feedback",
"status": "pass" if all(r["target_met"] for r in results) else "warn",
"kpis": results,
}
json.dump(report, open(output_path, "w", encoding="utf-8"), indent=2)
print(f"KPI_REPORT status={report['status']} output={output_path}")
PY
+89
View File
@@ -0,0 +1,89 @@
#!/usr/bin/env bash
set -euo pipefail
# CASAN Level 5 unified harness wrapper.
# Usage:
# casan-harness.sh <input-file> <output-file> [action-name] [-- <command> ...]
#
# Flow:
# H4 input security -> H5 governance -> H6 metrics around execution/cache -> H4 output filter
INPUT_FILE="${1:-}"
FINAL_OUTPUT="${2:-}"
ACTION_NAME="${3:-agent_step}"
shift 3 || true
if [[ "${1:-}" == "--" ]]; then
shift
fi
if [[ -z "$INPUT_FILE" || -z "$FINAL_OUTPUT" ]]; then
echo "Usage: casan-harness.sh <input-file> <output-file> [action-name] [-- <command> ...]" >&2
exit 64
fi
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
TMP_DIR="$PROJECT_ROOT/.specify/logs/tmp"
CACHE_DIR="$PROJECT_ROOT/.specify/logs/idempotency"
mkdir -p "$TMP_DIR" "$CACHE_DIR" "$(dirname "$FINAL_OUTPUT")"
hash_text() {
if command -v sha256sum >/dev/null 2>&1; then
sha256sum | awk '{print $1}'
else
shasum -a 256 | awk '{print $1}'
fi
}
CMD_STR="${*:-no_cmd}"
INPUT_HASH="$(cat "$INPUT_FILE" | hash_text)"
CMD_HASH="$(printf '%s' "$CMD_STR" | hash_text)"
IDEMPOTENCY_KEY="$(printf '%s|%s|%s' "$INPUT_HASH" "$CMD_HASH" "$ACTION_NAME" | hash_text)"
CACHE_META="$CACHE_DIR/$IDEMPOTENCY_KEY.json"
CACHE_OUT="$CACHE_DIR/$IDEMPOTENCY_KEY.output"
TRACE_SUFFIX="$(date +%s)-$$"
SAFE_INPUT="$TMP_DIR/security-input-$TRACE_SUFFIX.txt"
APPROVED_INPUT="$TMP_DIR/governance-approved-$TRACE_SUFFIX.txt"
RAW_OUTPUT="$TMP_DIR/raw-output-$TRACE_SUFFIX.txt"
"$SCRIPT_DIR/security-check.sh" "$INPUT_FILE" "$SAFE_INPUT" input
"$SCRIPT_DIR/governance-check.sh" "$SAFE_INPUT" "$APPROVED_INPUT" "$ACTION_NAME"
# H2 tool registry gate is in the line of fire for side-effecting actions:
# it enforces idempotency key, per-agent permission, and rollback strategy
# before the command is allowed to execute. The wrapper already derived a
# content-addressed idempotency key above.
case "$ACTION_NAME" in
write_code|migration|deploy|db_write|external_api|write_file)
CASAN_IDEMPOTENCY_KEY="$IDEMPOTENCY_KEY" "$SCRIPT_DIR/tool-registry-gate.sh" "$ACTION_NAME"
;;
esac
if [[ -f "$CACHE_META" && -f "$CACHE_OUT" ]]; then
"$SCRIPT_DIR/agent-metrics.sh" "$APPROVED_INPUT" "$RAW_OUTPUT" -- bash -c 'cp "$1" "$CASAN_OUTPUT"' _ "$CACHE_OUT"
CACHE_STATUS="cached"
elif [[ "$#" -gt 0 ]]; then
"$SCRIPT_DIR/agent-metrics.sh" "$APPROVED_INPUT" "$RAW_OUTPUT" -- "$@"
CACHE_STATUS="stored"
else
"$SCRIPT_DIR/agent-metrics.sh" "$APPROVED_INPUT" "$RAW_OUTPUT"
CACHE_STATUS="stored"
fi
"$SCRIPT_DIR/security-check.sh" "$RAW_OUTPUT" "$FINAL_OUTPUT" output
if [[ "$CACHE_STATUS" == "stored" ]]; then
cat <<EOF > "$CACHE_META"
{
"idempotency_key": "$IDEMPOTENCY_KEY",
"timestamp": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")",
"action": "$ACTION_NAME",
"command": "$(printf '%s' "$CMD_STR" | sed 's/"/\\"/g')",
"output_hash": "$(cat "$FINAL_OUTPUT" | hash_text)"
}
EOF
cp "$FINAL_OUTPUT" "$CACHE_OUT"
fi
echo "CASAN_HARNESS_COMPLETE cache=$CACHE_STATUS key=$IDEMPOTENCY_KEY output=$FINAL_OUTPUT"
@@ -0,0 +1,166 @@
#!/usr/bin/env bash
# Consolidated prerequisite checking script
#
# This script provides unified prerequisite checking for Spec-Driven Development workflow.
# It replaces the functionality previously spread across multiple scripts.
#
# Usage: ./check-prerequisites.sh [OPTIONS]
#
# OPTIONS:
# --json Output in JSON format
# --require-tasks Require tasks.md to exist (for implementation phase)
# --include-tasks Include tasks.md in AVAILABLE_DOCS list
# --paths-only Only output path variables (no validation)
# --help, -h Show help message
#
# OUTPUTS:
# JSON mode: {"FEATURE_DIR":"...", "AVAILABLE_DOCS":["..."]}
# Text mode: FEATURE_DIR:... \n AVAILABLE_DOCS: \n ✓/✗ file.md
# Paths only: REPO_ROOT: ... \n BRANCH: ... \n FEATURE_DIR: ... etc.
set -e
# Parse command line arguments
JSON_MODE=false
REQUIRE_TASKS=false
INCLUDE_TASKS=false
PATHS_ONLY=false
for arg in "$@"; do
case "$arg" in
--json)
JSON_MODE=true
;;
--require-tasks)
REQUIRE_TASKS=true
;;
--include-tasks)
INCLUDE_TASKS=true
;;
--paths-only)
PATHS_ONLY=true
;;
--help|-h)
cat << 'EOF'
Usage: check-prerequisites.sh [OPTIONS]
Consolidated prerequisite checking for Spec-Driven Development workflow.
OPTIONS:
--json Output in JSON format
--require-tasks Require tasks.md to exist (for implementation phase)
--include-tasks Include tasks.md in AVAILABLE_DOCS list
--paths-only Only output path variables (no prerequisite validation)
--help, -h Show this help message
EXAMPLES:
# Check task prerequisites (plan.md required)
./check-prerequisites.sh --json
# Check implementation prerequisites (plan.md + tasks.md required)
./check-prerequisites.sh --json --require-tasks --include-tasks
# Get feature paths only (no validation)
./check-prerequisites.sh --paths-only
EOF
exit 0
;;
*)
echo "ERROR: Unknown option '$arg'. Use --help for usage information." >&2
exit 1
;;
esac
done
# Source common functions
SCRIPT_DIR="$(CDPATH="" cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/common.sh"
# Get feature paths and validate branch
eval $(get_feature_paths)
check_feature_branch "$CURRENT_BRANCH" "$HAS_GIT" || exit 1
# If paths-only mode, output paths and exit (support JSON + paths-only combined)
if $PATHS_ONLY; then
if $JSON_MODE; then
# Minimal JSON paths payload (no validation performed)
printf '{"REPO_ROOT":"%s","BRANCH":"%s","FEATURE_DIR":"%s","FEATURE_SPEC":"%s","IMPL_PLAN":"%s","TASKS":"%s"}\n' \
"$REPO_ROOT" "$CURRENT_BRANCH" "$FEATURE_DIR" "$FEATURE_SPEC" "$IMPL_PLAN" "$TASKS"
else
echo "REPO_ROOT: $REPO_ROOT"
echo "BRANCH: $CURRENT_BRANCH"
echo "FEATURE_DIR: $FEATURE_DIR"
echo "FEATURE_SPEC: $FEATURE_SPEC"
echo "IMPL_PLAN: $IMPL_PLAN"
echo "TASKS: $TASKS"
fi
exit 0
fi
# Validate required directories and files
if [[ ! -d "$FEATURE_DIR" ]]; then
echo "ERROR: Feature directory not found: $FEATURE_DIR" >&2
echo "Run /speckit.specify first to create the feature structure." >&2
exit 1
fi
if [[ ! -f "$IMPL_PLAN" ]]; then
echo "ERROR: plan.md not found in $FEATURE_DIR" >&2
echo "Run /speckit.plan first to create the implementation plan." >&2
exit 1
fi
# Check for tasks.md if required
if $REQUIRE_TASKS && [[ ! -f "$TASKS" ]]; then
echo "ERROR: tasks.md not found in $FEATURE_DIR" >&2
echo "Run /speckit.tasks first to create the task list." >&2
exit 1
fi
# Build list of available documents
docs=()
# Always check these optional docs
[[ -f "$RESEARCH" ]] && docs+=("research.md")
[[ -f "$DATA_MODEL" ]] && docs+=("data-model.md")
# Check contracts directory (only if it exists and has files)
if [[ -d "$CONTRACTS_DIR" ]] && [[ -n "$(ls -A "$CONTRACTS_DIR" 2>/dev/null)" ]]; then
docs+=("contracts/")
fi
[[ -f "$QUICKSTART" ]] && docs+=("quickstart.md")
# Include tasks.md if requested and it exists
if $INCLUDE_TASKS && [[ -f "$TASKS" ]]; then
docs+=("tasks.md")
fi
# Output results
if $JSON_MODE; then
# Build JSON array of documents
if [[ ${#docs[@]} -eq 0 ]]; then
json_docs="[]"
else
json_docs=$(printf '"%s",' "${docs[@]}")
json_docs="[${json_docs%,}]"
fi
printf '{"FEATURE_DIR":"%s","AVAILABLE_DOCS":%s}\n' "$FEATURE_DIR" "$json_docs"
else
# Text output
echo "FEATURE_DIR:$FEATURE_DIR"
echo "AVAILABLE_DOCS:"
# Show status of each potential document
check_file "$RESEARCH" "research.md"
check_file "$DATA_MODEL" "data-model.md"
check_dir "$CONTRACTS_DIR" "contracts/"
check_file "$QUICKSTART" "quickstart.md"
if $INCLUDE_TASKS; then
check_file "$TASKS" "tasks.md"
fi
fi
+156
View File
@@ -0,0 +1,156 @@
#!/usr/bin/env bash
# Common functions and variables for all scripts
# Get repository root, with fallback for non-git repositories
get_repo_root() {
if git rev-parse --show-toplevel >/dev/null 2>&1; then
git rev-parse --show-toplevel
else
# Fall back to script location for non-git repos
local script_dir="$(CDPATH="" cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
(cd "$script_dir/../../.." && pwd)
fi
}
# Get current branch, with fallback for non-git repositories
get_current_branch() {
# First check if SPECIFY_FEATURE environment variable is set
if [[ -n "${SPECIFY_FEATURE:-}" ]]; then
echo "$SPECIFY_FEATURE"
return
fi
# Then check git if available
if git rev-parse --abbrev-ref HEAD >/dev/null 2>&1; then
git rev-parse --abbrev-ref HEAD
return
fi
# For non-git repos, try to find the latest feature directory
local repo_root=$(get_repo_root)
local specs_dir="$repo_root/specs"
if [[ -d "$specs_dir" ]]; then
local latest_feature=""
local highest=0
for dir in "$specs_dir"/*; do
if [[ -d "$dir" ]]; then
local dirname=$(basename "$dir")
if [[ "$dirname" =~ ^([0-9]{3})- ]]; then
local number=${BASH_REMATCH[1]}
number=$((10#$number))
if [[ "$number" -gt "$highest" ]]; then
highest=$number
latest_feature=$dirname
fi
fi
fi
done
if [[ -n "$latest_feature" ]]; then
echo "$latest_feature"
return
fi
fi
echo "main" # Final fallback
}
# Check if we have git available
has_git() {
git rev-parse --show-toplevel >/dev/null 2>&1
}
check_feature_branch() {
local branch="$1"
local has_git_repo="$2"
# For non-git repos, we can't enforce branch naming but still provide output
if [[ "$has_git_repo" != "true" ]]; then
echo "[specify] Warning: Git repository not detected; skipped branch validation" >&2
return 0
fi
if [[ ! "$branch" =~ ^[0-9]{3}- ]]; then
echo "ERROR: Not on a feature branch. Current branch: $branch" >&2
echo "Feature branches should be named like: 001-feature-name" >&2
return 1
fi
return 0
}
get_feature_dir() { echo "$1/specs/$2"; }
# Find feature directory by numeric prefix instead of exact branch match
# This allows multiple branches to work on the same spec (e.g., 004-fix-bug, 004-add-feature)
find_feature_dir_by_prefix() {
local repo_root="$1"
local branch_name="$2"
local specs_dir="$repo_root/specs"
# Extract numeric prefix from branch (e.g., "004" from "004-whatever")
if [[ ! "$branch_name" =~ ^([0-9]{3})- ]]; then
# If branch doesn't have numeric prefix, fall back to exact match
echo "$specs_dir/$branch_name"
return
fi
local prefix="${BASH_REMATCH[1]}"
# Search for directories in specs/ that start with this prefix
local matches=()
if [[ -d "$specs_dir" ]]; then
for dir in "$specs_dir"/"$prefix"-*; do
if [[ -d "$dir" ]]; then
matches+=("$(basename "$dir")")
fi
done
fi
# Handle results
if [[ ${#matches[@]} -eq 0 ]]; then
# No match found - return the branch name path (will fail later with clear error)
echo "$specs_dir/$branch_name"
elif [[ ${#matches[@]} -eq 1 ]]; then
# Exactly one match - perfect!
echo "$specs_dir/${matches[0]}"
else
# Multiple matches - this shouldn't happen with proper naming convention
echo "ERROR: Multiple spec directories found with prefix '$prefix': ${matches[*]}" >&2
echo "Please ensure only one spec directory exists per numeric prefix." >&2
echo "$specs_dir/$branch_name" # Return something to avoid breaking the script
fi
}
get_feature_paths() {
local repo_root=$(get_repo_root)
local current_branch=$(get_current_branch)
local has_git_repo="false"
if has_git; then
has_git_repo="true"
fi
# Use prefix-based lookup to support multiple branches per spec
local feature_dir=$(find_feature_dir_by_prefix "$repo_root" "$current_branch")
cat <<EOF
REPO_ROOT='$repo_root'
CURRENT_BRANCH='$current_branch'
HAS_GIT='$has_git_repo'
FEATURE_DIR='$feature_dir'
FEATURE_SPEC='$feature_dir/spec.md'
IMPL_PLAN='$feature_dir/plan.md'
TASKS='$feature_dir/tasks.md'
RESEARCH='$feature_dir/research.md'
DATA_MODEL='$feature_dir/data-model.md'
QUICKSTART='$feature_dir/quickstart.md'
CONTRACTS_DIR='$feature_dir/contracts'
EOF
}
check_file() { [[ -f "$1" ]] && echo " ✓ $2" || echo " ✗ $2"; }
check_dir() { [[ -d "$1" && -n $(ls -A "$1" 2>/dev/null) ]] && echo " ✓ $2" || echo " ✗ $2"; }
@@ -0,0 +1,56 @@
#!/usr/bin/env bash
set -euo pipefail
# CASAN H1 context validator.
# Before a sub-agent trusts pipeline-context.yaml, verify every referenced
# artifact / trace file actually exists on disk, and (optionally) is not
# staler than CASAN_CONTEXT_TTL_SECONDS relative to the context file itself.
# Catches renamed/deleted/missing artifacts before they cause a silent bad read.
#
# Usage: context-validate.sh <pipeline-context.yaml>
# Exit: 0 all good, 2 a referenced path is missing, 3 a referenced path is stale.
CTX="${1:-}"
if [[ -z "$CTX" || ! -f "$CTX" ]]; then
echo "Usage: context-validate.sh <pipeline-context.yaml>" >&2
exit 64
fi
CTX_DIR="$(cd "$(dirname "$CTX")" && pwd)"
# Resolve paths relative to the repo root (3 levels up from this script).
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
python3 - "$CTX" "$PROJECT_ROOT" "${CASAN_CONTEXT_TTL_SECONDS:-0}" <<'PY'
import os, re, sys
ctx, root, ttl = sys.argv[1], sys.argv[2], int(sys.argv[3])
ctx_mtime = os.path.getmtime(ctx)
missing, stale, checked = [], [], 0
with open(ctx, encoding="utf-8") as fh:
for line in fh:
m = re.match(r"\s*(?:artifact|trace_file|path|data-model|spec|plan):\s*(\S+)", line)
if not m:
continue
ref = m.group(1).strip().strip('"').strip("'")
if ref in ("", "null", "<from", "pipeline-context>"):
continue
if "<" in ref or ref.endswith(">"):
continue # unresolved template placeholder, not a concrete path
cand = ref if os.path.isabs(ref) else os.path.join(root, ref)
checked += 1
if not os.path.exists(cand):
missing.append(ref)
continue
if ttl > 0 and (ctx_mtime - os.path.getmtime(cand)) > ttl:
stale.append(ref)
if missing:
sys.stderr.write("CONTEXT_INVALID missing=%d: %s\n" % (len(missing), ", ".join(missing)))
raise SystemExit(2)
if stale:
sys.stderr.write("CONTEXT_STALE stale=%d: %s\n" % (len(stale), ", ".join(stale)))
raise SystemExit(3)
print(f"CONTEXT_VALID checked={checked} all referenced artifacts present")
PY
@@ -0,0 +1,313 @@
#!/usr/bin/env bash
set -e
JSON_MODE=false
SHORT_NAME=""
BRANCH_NUMBER=""
ARGS=()
i=1
while [ $i -le $# ]; do
arg="${!i}"
case "$arg" in
--json)
JSON_MODE=true
;;
--short-name)
if [ $((i + 1)) -gt $# ]; then
echo 'Error: --short-name requires a value' >&2
exit 1
fi
i=$((i + 1))
next_arg="${!i}"
# Check if the next argument is another option (starts with --)
if [[ "$next_arg" == --* ]]; then
echo 'Error: --short-name requires a value' >&2
exit 1
fi
SHORT_NAME="$next_arg"
;;
--number)
if [ $((i + 1)) -gt $# ]; then
echo 'Error: --number requires a value' >&2
exit 1
fi
i=$((i + 1))
next_arg="${!i}"
if [[ "$next_arg" == --* ]]; then
echo 'Error: --number requires a value' >&2
exit 1
fi
BRANCH_NUMBER="$next_arg"
;;
--help|-h)
echo "Usage: $0 [--json] [--short-name <name>] [--number N] <feature_description>"
echo ""
echo "Options:"
echo " --json Output in JSON format"
echo " --short-name <name> Provide a custom short name (2-4 words) for the branch"
echo " --number N Specify branch number manually (overrides auto-detection)"
echo " --help, -h Show this help message"
echo ""
echo "Examples:"
echo " $0 'Add user authentication system' --short-name 'user-auth'"
echo " $0 'Implement OAuth2 integration for API' --number 5"
exit 0
;;
*)
ARGS+=("$arg")
;;
esac
i=$((i + 1))
done
FEATURE_DESCRIPTION="${ARGS[*]}"
if [ -z "$FEATURE_DESCRIPTION" ]; then
echo "Usage: $0 [--json] [--short-name <name>] [--number N] <feature_description>" >&2
exit 1
fi
# Trim whitespace and validate description is not empty (e.g., user passed only whitespace)
FEATURE_DESCRIPTION=$(echo "$FEATURE_DESCRIPTION" | xargs)
if [ -z "$FEATURE_DESCRIPTION" ]; then
echo "Error: Feature description cannot be empty or contain only whitespace" >&2
exit 1
fi
# Function to find the repository root by searching for existing project markers
find_repo_root() {
local dir="$1"
while [ "$dir" != "/" ]; do
if [ -d "$dir/.git" ] || [ -d "$dir/.specify" ]; then
echo "$dir"
return 0
fi
dir="$(dirname "$dir")"
done
return 1
}
# Function to get highest number from specs directory
get_highest_from_specs() {
local specs_dir="$1"
local highest=0
if [ -d "$specs_dir" ]; then
for dir in "$specs_dir"/*; do
[ -d "$dir" ] || continue
dirname=$(basename "$dir")
number=$(echo "$dirname" | grep -o '^[0-9]\+' || echo "0")
number=$((10#$number))
if [ "$number" -gt "$highest" ]; then
highest=$number
fi
done
fi
echo "$highest"
}
# Function to get highest number from git branches
get_highest_from_branches() {
local highest=0
# Get all branches (local and remote)
branches=$(git branch -a 2>/dev/null || echo "")
if [ -n "$branches" ]; then
while IFS= read -r branch; do
# Clean branch name: remove leading markers and remote prefixes
clean_branch=$(echo "$branch" | sed 's/^[* ]*//; s|^remotes/[^/]*/||')
# Extract feature number if branch matches pattern ###-*
if echo "$clean_branch" | grep -q '^[0-9]\{3\}-'; then
number=$(echo "$clean_branch" | grep -o '^[0-9]\{3\}' || echo "0")
number=$((10#$number))
if [ "$number" -gt "$highest" ]; then
highest=$number
fi
fi
done <<< "$branches"
fi
echo "$highest"
}
# Function to check existing branches (local and remote) and return next available number
check_existing_branches() {
local specs_dir="$1"
# Fetch all remotes to get latest branch info (suppress errors if no remotes)
git fetch --all --prune 2>/dev/null || true
# Get highest number from ALL branches (not just matching short name)
local highest_branch=$(get_highest_from_branches)
# Get highest number from ALL specs (not just matching short name)
local highest_spec=$(get_highest_from_specs "$specs_dir")
# Take the maximum of both
local max_num=$highest_branch
if [ "$highest_spec" -gt "$max_num" ]; then
max_num=$highest_spec
fi
# Return next number
echo $((max_num + 1))
}
# Function to clean and format a branch name
clean_branch_name() {
local name="$1"
echo "$name" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/-\+/-/g' | sed 's/^-//' | sed 's/-$//'
}
# Resolve repository root. Prefer git information when available, but fall back
# to searching for repository markers so the workflow still functions in repositories that
# were initialised with --no-git.
SCRIPT_DIR="$(CDPATH="" cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if git rev-parse --show-toplevel >/dev/null 2>&1; then
REPO_ROOT=$(git rev-parse --show-toplevel)
HAS_GIT=true
else
REPO_ROOT="$(find_repo_root "$SCRIPT_DIR")"
if [ -z "$REPO_ROOT" ]; then
echo "Error: Could not determine repository root. Please run this script from within the repository." >&2
exit 1
fi
HAS_GIT=false
fi
cd "$REPO_ROOT"
SPECS_DIR="$REPO_ROOT/specs"
mkdir -p "$SPECS_DIR"
# Function to generate branch name with stop word filtering and length filtering
generate_branch_name() {
local description="$1"
# Common stop words to filter out
local stop_words="^(i|a|an|the|to|for|of|in|on|at|by|with|from|is|are|was|were|be|been|being|have|has|had|do|does|did|will|would|should|could|can|may|might|must|shall|this|that|these|those|my|your|our|their|want|need|add|get|set)$"
# Convert to lowercase and split into words
local clean_name=$(echo "$description" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/ /g')
# Filter words: remove stop words and words shorter than 3 chars (unless they're uppercase acronyms in original)
local meaningful_words=()
for word in $clean_name; do
# Skip empty words
[ -z "$word" ] && continue
# Keep words that are NOT stop words AND (length >= 3 OR are potential acronyms)
if ! echo "$word" | grep -qiE "$stop_words"; then
if [ ${#word} -ge 3 ]; then
meaningful_words+=("$word")
elif echo "$description" | grep -q "\b${word^^}\b"; then
# Keep short words if they appear as uppercase in original (likely acronyms)
meaningful_words+=("$word")
fi
fi
done
# If we have meaningful words, use first 3-4 of them
if [ ${#meaningful_words[@]} -gt 0 ]; then
local max_words=3
if [ ${#meaningful_words[@]} -eq 4 ]; then max_words=4; fi
local result=""
local count=0
for word in "${meaningful_words[@]}"; do
if [ $count -ge $max_words ]; then break; fi
if [ -n "$result" ]; then result="$result-"; fi
result="$result$word"
count=$((count + 1))
done
echo "$result"
else
# Fallback to original logic if no meaningful words found
local cleaned=$(clean_branch_name "$description")
echo "$cleaned" | tr '-' '\n' | grep -v '^$' | head -3 | tr '\n' '-' | sed 's/-$//'
fi
}
# Generate branch name
if [ -n "$SHORT_NAME" ]; then
# Use provided short name, just clean it up
BRANCH_SUFFIX=$(clean_branch_name "$SHORT_NAME")
else
# Generate from description with smart filtering
BRANCH_SUFFIX=$(generate_branch_name "$FEATURE_DESCRIPTION")
fi
# Determine branch number
if [ -z "$BRANCH_NUMBER" ]; then
if [ "$HAS_GIT" = true ]; then
# Check existing branches on remotes
BRANCH_NUMBER=$(check_existing_branches "$SPECS_DIR")
else
# Fall back to local directory check
HIGHEST=$(get_highest_from_specs "$SPECS_DIR")
BRANCH_NUMBER=$((HIGHEST + 1))
fi
fi
# Force base-10 interpretation to prevent octal conversion (e.g., 010 → 8 in octal, but should be 10 in decimal)
FEATURE_NUM=$(printf "%03d" "$((10#$BRANCH_NUMBER))")
BRANCH_NAME="${FEATURE_NUM}-${BRANCH_SUFFIX}"
# GitHub enforces a 244-byte limit on branch names
# Validate and truncate if necessary
MAX_BRANCH_LENGTH=244
if [ ${#BRANCH_NAME} -gt $MAX_BRANCH_LENGTH ]; then
# Calculate how much we need to trim from suffix
# Account for: feature number (3) + hyphen (1) = 4 chars
MAX_SUFFIX_LENGTH=$((MAX_BRANCH_LENGTH - 4))
# Truncate suffix at word boundary if possible
TRUNCATED_SUFFIX=$(echo "$BRANCH_SUFFIX" | cut -c1-$MAX_SUFFIX_LENGTH)
# Remove trailing hyphen if truncation created one
TRUNCATED_SUFFIX=$(echo "$TRUNCATED_SUFFIX" | sed 's/-$//')
ORIGINAL_BRANCH_NAME="$BRANCH_NAME"
BRANCH_NAME="${FEATURE_NUM}-${TRUNCATED_SUFFIX}"
>&2 echo "[specify] Warning: Branch name exceeded GitHub's 244-byte limit"
>&2 echo "[specify] Original: $ORIGINAL_BRANCH_NAME (${#ORIGINAL_BRANCH_NAME} bytes)"
>&2 echo "[specify] Truncated to: $BRANCH_NAME (${#BRANCH_NAME} bytes)"
fi
if [ "$HAS_GIT" = true ]; then
if ! git checkout -b "$BRANCH_NAME" 2>/dev/null; then
# Check if branch already exists
if git branch --list "$BRANCH_NAME" | grep -q .; then
>&2 echo "Error: Branch '$BRANCH_NAME' already exists. Please use a different feature name or specify a different number with --number."
exit 1
else
>&2 echo "Error: Failed to create git branch '$BRANCH_NAME'. Please check your git configuration and try again."
exit 1
fi
fi
else
>&2 echo "[specify] Warning: Git repository not detected; skipped branch creation for $BRANCH_NAME"
fi
FEATURE_DIR="$SPECS_DIR/$BRANCH_NAME"
mkdir -p "$FEATURE_DIR"
TEMPLATE="$REPO_ROOT/.specify/templates/spec-template.md"
SPEC_FILE="$FEATURE_DIR/spec.md"
if [ -f "$TEMPLATE" ]; then cp "$TEMPLATE" "$SPEC_FILE"; else touch "$SPEC_FILE"; fi
# Set the SPECIFY_FEATURE environment variable for the current session
export SPECIFY_FEATURE="$BRANCH_NAME"
if $JSON_MODE; then
printf '{"BRANCH_NAME":"%s","SPEC_FILE":"%s","FEATURE_NUM":"%s"}\n' "$BRANCH_NAME" "$SPEC_FILE" "$FEATURE_NUM"
else
echo "BRANCH_NAME: $BRANCH_NAME"
echo "SPEC_FILE: $SPEC_FILE"
echo "FEATURE_NUM: $FEATURE_NUM"
echo "SPECIFY_FEATURE environment variable set to: $BRANCH_NAME"
fi
+66
View File
@@ -0,0 +1,66 @@
#!/usr/bin/env bash
set -euo pipefail
# CASAN Level 5 drift detector.
# Usage:
# drift-detect.sh <golden-file> <candidate-file> <report-json>
GOLDEN="${1:-}"
CANDIDATE="${2:-}"
REPORT="${3:-}"
if [[ -z "$GOLDEN" || -z "$CANDIDATE" || -z "$REPORT" ]]; then
echo "Usage: drift-detect.sh <golden-file> <candidate-file> <report-json>" >&2
exit 64
fi
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
mkdir -p "$(dirname "$REPORT")" "$PROJECT_ROOT/.specify/logs/level5"
python3 - "$GOLDEN" "$CANDIDATE" "$REPORT" <<'PY'
import difflib
import hashlib
import json
import pathlib
import sys
from datetime import datetime, timezone
golden_path = pathlib.Path(sys.argv[1])
candidate_path = pathlib.Path(sys.argv[2])
report_path = pathlib.Path(sys.argv[3])
golden = golden_path.read_text(encoding="utf-8")
candidate = candidate_path.read_text(encoding="utf-8")
similarity = difflib.SequenceMatcher(None, golden, candidate).ratio()
length_delta = abs(len(candidate) - len(golden)) / max(len(golden), 1)
status = "pass"
action = "allow"
if similarity < 0.70 or length_delta > 0.50:
status = "fail"
action = "block_or_fallback"
elif similarity < 0.85 or length_delta > 0.30:
status = "warn"
action = "require_review"
report = {
"timestamp": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
"harness": "L5-drift-detection",
"status": status,
"action": action,
"similarity_ratio": round(similarity, 4),
"length_delta_ratio": round(length_delta, 4),
"golden_hash": hashlib.sha256(golden.encode()).hexdigest(),
"candidate_hash": hashlib.sha256(candidate.encode()).hexdigest(),
"golden_file": str(golden_path),
"candidate_file": str(candidate_path),
}
report_path.write_text(json.dumps(report, indent=2) + "\n", encoding="utf-8")
print(f"DRIFT_{status.upper()} similarity={report['similarity_ratio']} length_delta={report['length_delta_ratio']} report={report_path}")
if status == "fail":
raise SystemExit(2)
PY
@@ -0,0 +1,173 @@
#!/usr/bin/env bash
set -euo pipefail
# CASAN H5 Governance Harness
# Usage:
# governance-check.sh <input-file> <output-file> [action-name]
#
# Non-interactive by default. High-risk actions are denied unless:
# CASAN_APPROVAL_DECISION=approve CASAN_APPROVER=<name>
INPUT_FILE="${1:-}"
OUTPUT_FILE="${2:-}"
ACTION_NAME="${3:-agent_step}"
if [[ -z "$INPUT_FILE" || -z "$OUTPUT_FILE" ]]; then
echo "Usage: governance-check.sh <input-file> <output-file> [action-name]" >&2
exit 64
fi
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
LOG_DIR="$PROJECT_ROOT/.specify/logs"
TRACE_DIR="$LOG_DIR/trace"
AUDIT_DIR="$LOG_DIR/audit"
mkdir -p "$TRACE_DIR" "$AUDIT_DIR" "$(dirname "$OUTPUT_FILE")"
if [[ ! -f "$INPUT_FILE" ]]; then
echo "GOVERNANCE_DENIED: input file not found: $INPUT_FILE" >&2
exit 1
fi
timestamp() {
date -u +"%Y-%m-%dT%H:%M:%SZ"
}
new_trace_id() {
if command -v uuidgen >/dev/null 2>&1; then
uuidgen | tr '[:upper:]' '[:lower:]'
else
printf 'trace-%s-%s\n' "$(date +%s)" "$$"
fi
}
hash_text() {
if command -v sha256sum >/dev/null 2>&1; then
sha256sum | awk '{print $1}'
else
shasum -a 256 | awk '{print $1}'
fi
}
json_escape() {
python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))' 2>/dev/null || sed 's/\\/\\\\/g; s/"/\\"/g'
}
TRACE_ID="$(new_trace_id)"
TIMESTAMP="$(timestamp)"
INPUT="$(cat "$INPUT_FILE")"
LOWER_INPUT="$(printf '%s' "$INPUT" | tr '[:upper:]' '[:lower:]')"
ACTOR="${CASAN_ACTOR:-developer}"
APPROVER="${CASAN_APPROVER:-}"
APPROVAL_DECISION="${CASAN_APPROVAL_DECISION:-auto}"
AUDIT_LOG="$AUDIT_DIR/audit.jsonl"
RISK_LEVEL="low"
REASONS=()
case "$ACTION_NAME" in
deploy|launch|write_code|write_file|migration|db_write|external_api|tool_call)
RISK_LEVEL="medium"
REASONS+=("sensitive-action:$ACTION_NAME")
;;
esac
if printf '%s' "$LOWER_INPUT" | grep -Eq "(delete|drop table|password|api[_-]?key|secret|token|credential|migration|deploy|external api|shutdown|dump database)"; then
RISK_LEVEL="high"
REASONS+=("high-risk-content")
elif printf '%s' "$LOWER_INPUT" | grep -Eq "(internal|config|system|policy|permission)"; then
[[ "$RISK_LEVEL" == "low" ]] && RISK_LEVEL="medium"
REASONS+=("medium-risk-content")
fi
APPROVAL_STATUS="auto_approved"
DECISION="approved"
if [[ "$RISK_LEVEL" == "medium" ]]; then
APPROVAL_STATUS="policy_auto_approved_with_audit"
fi
if [[ "$RISK_LEVEL" == "high" ]]; then
if [[ "$APPROVAL_DECISION" == "approve" && -n "$APPROVER" ]]; then
if [[ "$APPROVER" == "$ACTOR" ]]; then
# Separation of duties: the submitter may not approve their own action.
APPROVAL_STATUS="separation_of_duties_violation"
DECISION="denied"
REASONS+=("separation-of-duties:actor-equals-approver")
else
APPROVAL_STATUS="human_approved"
DECISION="approved"
fi
else
APPROVAL_STATUS="approval_required"
DECISION="denied"
fi
fi
INPUT_HASH="$(printf '%s' "$INPUT" | hash_text)"
OUTPUT_CONTENT="$INPUT"
OUTPUT_HASH="$(printf '%s' "$OUTPUT_CONTENT" | hash_text)"
PREV_HASH=""
if [[ -s "$AUDIT_LOG" ]]; then
PREV_HASH="$(tail -n 1 "$AUDIT_LOG" | sed -n 's/.*"record_hash":"\([^"]*\)".*/\1/p')"
fi
REASONS_JSON="$(printf '%s\n' "${REASONS[@]:-}" | python3 -c 'import json,sys; print(json.dumps([x for x in sys.stdin.read().splitlines() if x]))')"
# approver and output_hash are part of the hashed core so they cannot be
# silently mutated after the fact.
RECORD_CORE="$(printf '%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s' "$TIMESTAMP" "$TRACE_ID" "$ACTION_NAME" "$ACTOR" "$RISK_LEVEL" "$DECISION" "$APPROVAL_STATUS" "$APPROVER" "$INPUT_HASH" "$OUTPUT_HASH" "$PREV_HASH")"
RECORD_HASH="$(printf '%s' "$RECORD_CORE" | hash_text)"
TRACE_FILE="$TRACE_DIR/governance-$TRACE_ID.json"
cat > "$TRACE_FILE" <<EOF
{
"trace_id": "$TRACE_ID",
"timestamp": "$TIMESTAMP",
"harness": "H5-governance",
"action": "$ACTION_NAME",
"actor": "$ACTOR",
"risk_level": "$RISK_LEVEL",
"decision": "$DECISION",
"approval_status": "$APPROVAL_STATUS",
"approver": "$APPROVER",
"reasons": $REASONS_JSON,
"input_hash": "$INPUT_HASH",
"output_hash": "$OUTPUT_HASH",
"previous_record_hash": "$PREV_HASH",
"record_hash": "$RECORD_HASH"
}
EOF
printf '{"timestamp":"%s","trace_id":"%s","harness":"H5-governance","action":"%s","actor":"%s","risk_level":"%s","decision":"%s","approval_status":"%s","approver":"%s","input_hash":"%s","output_hash":"%s","previous_record_hash":"%s","record_hash":"%s"}\n' \
"$TIMESTAMP" "$TRACE_ID" "$ACTION_NAME" "$ACTOR" "$RISK_LEVEL" "$DECISION" "$APPROVAL_STATUS" "$APPROVER" "$INPUT_HASH" "$OUTPUT_HASH" "$PREV_HASH" "$RECORD_HASH" >> "$AUDIT_LOG"
# --- External anchor: cryptographically sign the new chain head ---
# A re-forged chain (recomputed hashes) changes the head; without the private
# key the attacker cannot produce a matching signature, so verification fails.
# Production note: the private key must live off-repo (KMS/HSM). It is local
# here only for self-contained demonstration.
if command -v openssl >/dev/null 2>&1; then
# Private signing key lives OFF-REPO (default ~/.casan/audit-keys); only the
# public key is committed. Production: replace with KMS/HSM.
PUB_DIR="$PROJECT_ROOT/.specify/level5/central-governance"
PRIV_DIR="${CASAN_AUDIT_KEY_DIR:-$HOME/.casan/audit-keys}"
AUDIT_PRIV="$PRIV_DIR/audit-private.pem"
AUDIT_PUB="$PUB_DIR/audit-public.pem"
mkdir -p "$PUB_DIR" "$PRIV_DIR"
if [[ ! -f "$AUDIT_PRIV" ]]; then
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out "$AUDIT_PRIV" 2>/dev/null
chmod 600 "$AUDIT_PRIV"
openssl rsa -in "$AUDIT_PRIV" -pubout -out "$AUDIT_PUB" 2>/dev/null
fi
printf '%s' "$RECORD_HASH" > "$AUDIT_DIR/audit-head.txt"
openssl dgst -sha256 -sign "$AUDIT_PRIV" -out "$AUDIT_DIR/audit-head.sig" "$AUDIT_DIR/audit-head.txt" 2>/dev/null || true
fi
if [[ "$DECISION" != "approved" ]]; then
: > "$OUTPUT_FILE"
echo "GOVERNANCE_DENIED trace_id=$TRACE_ID risk=$RISK_LEVEL approval_status=$APPROVAL_STATUS" >&2
exit 2
fi
printf '%s\n' "$OUTPUT_CONTENT" > "$OUTPUT_FILE"
echo "GOVERNANCE_APPROVED trace_id=$TRACE_ID risk=$RISK_LEVEL approval_status=$APPROVAL_STATUS output=$OUTPUT_FILE"

Some files were not shown because too many files have changed in this diff Show More