update first - 84
This commit is contained in:
@@ -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"
|
||||
}
|
||||
Binary file not shown.
@@ -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"}
|
||||
Binary file not shown.
@@ -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"}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"idempotency_key": "a695c82f9d29aba2adace11fc766fd1b0ffae9ef27514df5907bdf66a69e0bba",
|
||||
"timestamp": "2026-06-29T17:01:11Z",
|
||||
"action": "agent_step",
|
||||
"command": "no_cmd",
|
||||
"output_hash": "054cd5120725af9fdf9a8033f7b0f60d2d8ba3861926b00686d74700668e5116"
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
Generate safe OKR plan for employee ***MASKED_EMAIL***.
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"idempotency_key": "d6d750d0ee93e4b9f9b917a42e3f7f6e8725c63bf412594778810bd91c175084",
|
||||
"timestamp": "2026-06-29T17:01:23Z",
|
||||
"action": "write_code",
|
||||
"command": "bash -c echo wrote",
|
||||
"output_hash": "01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b"
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
|
||||
@@ -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"}
|
||||
+1
@@ -0,0 +1 @@
|
||||
ORIGINAL
|
||||
@@ -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
|
||||
+22
@@ -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": ""
|
||||
}
|
||||
+22
@@ -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"
|
||||
}
|
||||
+22
@@ -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": ""
|
||||
}
|
||||
+22
@@ -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"
|
||||
}
|
||||
+22
@@ -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": ""
|
||||
}
|
||||
+22
@@ -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": ""
|
||||
}
|
||||
+22
@@ -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": ""
|
||||
}
|
||||
+22
@@ -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": ""
|
||||
}
|
||||
+22
@@ -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": ""
|
||||
}
|
||||
+16
@@ -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"
|
||||
}
|
||||
+16
@@ -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"
|
||||
}
|
||||
+16
@@ -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"
|
||||
}
|
||||
+16
@@ -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"
|
||||
}
|
||||
+16
@@ -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"
|
||||
}
|
||||
+16
@@ -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"
|
||||
}
|
||||
+16
@@ -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"
|
||||
}
|
||||
+16
@@ -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"
|
||||
}
|
||||
+12
@@ -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"
|
||||
}
|
||||
+12
@@ -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"
|
||||
}
|
||||
+12
@@ -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"
|
||||
}
|
||||
+12
@@ -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"
|
||||
}
|
||||
+12
@@ -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"
|
||||
}
|
||||
+12
@@ -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"
|
||||
}
|
||||
+12
@@ -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"
|
||||
}
|
||||
+12
@@ -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"
|
||||
}
|
||||
+12
@@ -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"
|
||||
}
|
||||
+12
@@ -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"
|
||||
}
|
||||
+12
@@ -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"
|
||||
}
|
||||
+12
@@ -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"
|
||||
}
|
||||
+12
@@ -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"
|
||||
}
|
||||
+12
@@ -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"
|
||||
}
|
||||
+12
@@ -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"
|
||||
}
|
||||
+12
@@ -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"
|
||||
}
|
||||
+12
@@ -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"
|
||||
}
|
||||
+12
@@ -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"
|
||||
}
|
||||
+12
@@ -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"
|
||||
}
|
||||
+12
@@ -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
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
Reference in New Issue
Block a user