@event4u/agent-config 16.0.0 → 16.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.augment-plugin/marketplace.json +3 -3
- package/.augment-plugin/plugin.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +289 -140
- package/CONTRIBUTING.md +9 -0
- package/README.md +2 -2
- package/dist/agent-src/commands/analyze/inbox.md +26 -0
- package/dist/agent-src/commands/analyze/roadmap-repos.md +27 -0
- package/dist/agent-src/commands/challenge-me/closure.md +178 -0
- package/dist/agent-src/commands/challenge-me.md +9 -2
- package/dist/agent-src/commands/council/default.md +25 -5
- package/dist/agent-src/commands/feature/plan.md +28 -1
- package/dist/agent-src/commands/feature/roadmap.md +28 -1
- package/dist/agent-src/commands/implement-ticket.md +27 -0
- package/dist/agent-src/commands/jira-ticket.md +27 -0
- package/dist/agent-src/commands/pr/create.md +29 -0
- package/dist/agent-src/commands/pr/merge.md +60 -9
- package/dist/agent-src/commands/roadmap/ai-council.md +7 -1
- package/dist/agent-src/commands/roadmap/create.md +74 -27
- package/dist/agent-src/commands/roadmap/materialize.md +27 -0
- package/dist/agent-src/commands/roadmap/process-full.md +123 -18
- package/dist/agent-src/commands/team.md +22 -0
- package/dist/agent-src/commands/work.md +4 -0
- package/dist/agent-src/contexts/authority/scope-growth-ownership.md +80 -0
- package/dist/agent-src/contexts/communication/rules-auto/user-interaction-mechanics.md +47 -0
- package/dist/agent-src/contexts/execution/auto-orchestration-activation.md +44 -3
- package/dist/agent-src/contexts/execution/autonomy-mechanics.md +19 -23
- package/dist/agent-src/contexts/execution/decision-closure-execution.md +93 -0
- package/dist/agent-src/contexts/execution/fix-loop-ladder.md +108 -0
- package/dist/agent-src/contexts/execution/plan-confidence-gate.md +4 -2
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +75 -6
- package/dist/agent-src/guidelines/agent-infra/active-remediation-mechanics.md +57 -0
- package/dist/agent-src/guidelines/agent-infra/evaluator-independence-mechanics.md +102 -0
- package/dist/agent-src/guidelines/design-fidelity-routing.md +29 -0
- package/dist/agent-src/rules/autonomous-execution.md +15 -8
- package/dist/agent-src/rules/delegation-policy.md +23 -45
- package/dist/agent-src/rules/design-fidelity.md +32 -37
- package/dist/agent-src/rules/evaluator-independence.md +43 -49
- package/dist/agent-src/rules/test-first.md +41 -0
- package/dist/agent-src/rules/ui-audit-gate.md +1 -2
- package/dist/agent-src/skills/accessibility-auditor/data/aria-patterns.csv +1 -1
- package/dist/agent-src/skills/accessibility-auditor/data/manifest.json +3 -3
- package/dist/agent-src/skills/ai-council/SKILL.md +11 -3
- package/dist/agent-src/skills/api-design/data/manifest.json +3 -3
- package/dist/agent-src/skills/database/data/manifest.json +4 -4
- package/dist/agent-src/skills/database/data/query-tuning.csv +3 -3
- package/dist/agent-src/skills/design-review/SKILL.md +80 -4
- package/dist/agent-src/skills/design-review/references/verification-automation.md +32 -0
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +30 -5
- package/dist/agent-src/skills/existing-ui-audit/references/component-taxonomy-detection.md +56 -0
- package/dist/agent-src/skills/experiment-loop/SKILL.md +31 -1
- package/dist/agent-src/skills/markitdown/SKILL.md +4 -0
- package/dist/agent-src/skills/playwright-testing/SKILL.md +30 -6
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +1 -1
- package/dist/agent-src/skills/storybook-workshop/SKILL.md +42 -3
- package/dist/agent-src/skills/threat-modeling/SKILL.md +9 -4
- package/dist/agent-src/skills/threat-modeling/data/manifest.json +4 -4
- package/dist/agent-src/skills/ui-component-architect/SKILL.md +23 -0
- package/dist/agent-src/skills/verify-repair-loop/SKILL.md +37 -3
- package/dist/agent-src/templates/agent-settings.md +1 -1
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/roadmaps.md +218 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +62 -2
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/test.ts +1 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +1 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +260 -21
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
- package/dist/agent-src/templates/scripts/work_engine/state.ts +57 -7
- package/dist/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
- package/dist/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
- package/dist/cli/python/knowledge_ingest.js +1 -1
- package/dist/cli/python/workspace_drive.js +1 -1
- package/dist/cli-delegate/check_update_banner.js +40 -2
- package/dist/cli-delegate/chunk-4E2W63DA.js +335 -0
- package/dist/cli-delegate/{chunk-S3VFXMTM.js → chunk-4XFP2Y3E.js} +1 -1
- package/dist/cli-delegate/chunk-5R7HOLAF.js +99 -0
- package/dist/cli-delegate/{chunk-IVBPRCQO.js → chunk-6KFEC3G7.js} +1 -1
- package/dist/cli-delegate/{chunk-2TAQ2K4V.js → chunk-6KM6ORRY.js} +55 -386
- package/dist/cli-delegate/{chunk-AK6HODNQ.js → chunk-7XGBX6ZH.js} +973 -555
- package/dist/cli-delegate/{chunk-2XY7ILBT.js → chunk-FC75PGJE.js} +42 -2
- package/dist/cli-delegate/{chunk-55ACLDUE.js → chunk-FP36SWJD.js} +587 -141
- package/dist/cli-delegate/{chunk-3FKR64R5.js → chunk-IGD6ERX6.js} +62 -7
- package/dist/cli-delegate/{chunk-GYC3VRYN.js → chunk-JNMVLWLR.js} +99 -14
- package/dist/cli-delegate/{chunk-H32DGUJH.js → chunk-P6ZHP5W2.js} +1 -1
- package/dist/cli-delegate/{chunk-57PN4GYJ.js → chunk-RGNIWTEA.js} +1 -1
- package/dist/cli-delegate/chunk-ROYYTEZU.js +39 -0
- package/dist/cli-delegate/{chunk-2DK2VJWG.js → chunk-WGXTVKCQ.js} +20 -6
- package/dist/cli-delegate/chunk-YDPILCOL.js +152 -0
- package/dist/cli-delegate/{chunk-QJWAUMJW.js → chunk-YJ72UIAK.js} +730 -15
- package/dist/cli-delegate/cmd_analyze_session.js +1 -1
- package/dist/cli-delegate/cmd_brand_status.js +1 -1
- package/dist/cli-delegate/cmd_conformance.js +985 -831
- package/dist/cli-delegate/cmd_doctor.js +7 -5
- package/dist/cli-delegate/cmd_explain.js +2 -2
- package/dist/cli-delegate/cmd_export.js +9 -7
- package/dist/cli-delegate/cmd_fleet.js +4 -2
- package/dist/cli-delegate/cmd_handoff.js +7 -7
- package/dist/cli-delegate/cmd_mcp_available.js +1 -1
- package/dist/cli-delegate/cmd_memory_get.js +1 -1
- package/dist/cli-delegate/cmd_migrate.js +2 -2
- package/dist/cli-delegate/cmd_packs_active.js +2 -2
- package/dist/cli-delegate/cmd_preflight.js +4 -2
- package/dist/cli-delegate/cmd_prune.js +10 -8
- package/dist/cli-delegate/cmd_route_audit.js +3 -3
- package/dist/cli-delegate/cmd_route_explain.js +1 -1
- package/dist/cli-delegate/cmd_session_recycle.js +4 -4
- package/dist/cli-delegate/cmd_settings_get.js +28 -2
- package/dist/cli-delegate/cmd_settings_migrate.js +9 -7
- package/dist/cli-delegate/cmd_settings_set.js +1 -1
- package/dist/cli-delegate/cmd_sync.js +10 -8
- package/dist/cli-delegate/cmd_uninstall.js +16 -13
- package/dist/cli-delegate/cmd_update.js +1 -1
- package/dist/cli-delegate/cmd_upgrade.js +1 -1
- package/dist/cli-delegate/cmd_validate.js +12 -10
- package/dist/cli-delegate/cmd_versions.js +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +90 -43
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +4 -4
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +10 -8
- package/dist/discovery/trust-report.md +3 -3
- package/dist/discovery/workspaces.json +8 -6
- package/dist/hooks/dispatch.js +7682 -2772
- package/dist/hooks/dispatch.sha256 +1 -0
- package/dist/install/claudeRuleRewrite.js +14 -3
- package/dist/install/conflict.js +60 -10
- package/dist/install/conflictTracker.js +218 -0
- package/dist/install/fsPrimitives.js +54 -0
- package/dist/install/install.mjs +2200 -1559
- package/dist/install/manifestFiles.js +259 -0
- package/dist/install/preflight.js +14 -2
- package/dist/install/preserve.js +157 -0
- package/dist/install/recordedOwnership.js +132 -0
- package/dist/install/txlog.js +11 -2
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +3066 -1470
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/agent_settings.js +3 -2
- package/dist/scripts/_lib/fs_atomic.js +167 -0
- package/dist/scripts/_lib/installed_tools.js +460 -0
- package/dist/scripts/_lib/lean_projection_mode.js +3 -2
- package/dist/scripts/_lib/settings_renamed_keys.js +78 -0
- package/dist/server/routes/install.js +8 -0
- package/dist/server/routes/wizard.js +9 -3
- package/dist/server/schemas/settings.js +40 -5
- package/dist/shared/missionExecution.js +97 -0
- package/dist/ui/assets/index-Cwq4d0hX.js +23 -0
- package/dist/ui/assets/index-Cyjy8bWO.css +1 -0
- package/dist/ui/index.html +2 -2
- package/docs/MIGRATION.md +103 -0
- package/docs/contracts/settings-classes.md +28 -7
- package/docs/guidelines/agent-infra/active-remediation-mechanics.md +57 -0
- package/docs/guidelines/agent-infra/evaluator-independence-mechanics.md +102 -0
- package/docs/guidelines/design-fidelity-routing.md +29 -0
- package/package.json +4 -4
- package/src/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +62 -2
- package/src/agent-src/templates/scripts/work_engine/directives/backend/test.ts +1 -0
- package/src/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +1 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
- package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +260 -21
- package/src/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
- package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
- package/src/agent-src/templates/scripts/work_engine/state.ts +57 -7
- package/src/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
- package/src/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
- package/src/config/agent-settings.template.yml +168 -27
- package/src/config/auto-rule-budget.json +16 -0
- package/src/config/ci-local-parity.yml +36 -5
- package/src/config/continuity-surface.json +23 -2
- package/src/config/gate-coverage.yml +377 -13
- package/src/config/gate-violation-baselines.json +10 -6
- package/src/config/hook-latency-budget.json +21 -0
- package/src/config/hook-token-budget.json +4 -2
- package/src/config/loop-surfaces.yaml +215 -0
- package/src/config/metric-registry.yml +114 -0
- package/src/config/placeholder-drift-budget.json +10 -0
- package/src/config/platform-anchor.json +3 -2
- package/src/config/release-gate-locality.yml +11 -0
- package/src/config/routing-coverage-seed.json +33 -5
- package/src/config/rule-enforcement-baseline.json +0 -1
- package/src/install/claudeRuleRewrite.ts +16 -2
- package/src/install/conflict.ts +65 -9
- package/src/install/conflictTracker.ts +299 -0
- package/src/install/fsPrimitives.ts +54 -0
- package/src/install/manifestFiles.ts +312 -0
- package/src/install/preflight.ts +16 -2
- package/src/install/preserve.ts +205 -0
- package/src/install/recordedOwnership.ts +149 -0
- package/src/install/txlog.ts +11 -2
- package/src/install/types.ts +19 -4
- package/src/scripts/_cli/cmd_conformance.ts +88 -15
- package/src/scripts/_cli/cmd_doctor.ts +7 -20
- package/src/scripts/_cli/cmd_route_explain.ts +9 -2
- package/src/scripts/_cli/cmd_settings_get.ts +44 -0
- package/src/scripts/_cli/doctor_execution.ts +173 -0
- package/src/scripts/_cli/doctor_network_posture.ts +226 -0
- package/src/scripts/_lib/activation_payload.ts +247 -0
- package/src/scripts/_lib/agent_settings.ts +4 -2
- package/src/scripts/_lib/authority_path.ts +102 -0
- package/src/scripts/_lib/body_portable.ts +102 -0
- package/src/scripts/_lib/cascade_base.ts +94 -0
- package/src/scripts/_lib/cc_transcript.ts +132 -1
- package/src/scripts/_lib/changelog_eras.ts +7 -2
- package/src/scripts/_lib/concern_sla_window.ts +200 -0
- package/src/scripts/_lib/continuation_ladder.ts +149 -2
- package/src/scripts/_lib/council_transport.ts +129 -0
- package/src/scripts/_lib/delivery_ready.ts +112 -0
- package/src/scripts/_lib/forge_protection.ts +381 -0
- package/src/scripts/_lib/forge_reader.ts +558 -0
- package/src/scripts/_lib/hook_platform_keys.ts +27 -0
- package/src/scripts/_lib/host_env_write.ts +111 -0
- package/src/scripts/_lib/json_python_parity.ts +127 -0
- package/src/scripts/_lib/lean_projection_mode.ts +14 -3
- package/src/scripts/_lib/llm_proposer_transport.ts +14 -4
- package/src/scripts/_lib/loop_surfaces.ts +382 -0
- package/src/scripts/_lib/mission_record.ts +190 -0
- package/src/scripts/_lib/obligation_frequency.ts +217 -0
- package/src/scripts/_lib/obligations.ts +513 -0
- package/src/scripts/_lib/orchestration_record.ts +11 -20
- package/src/scripts/_lib/probe_inputs.ts +271 -0
- package/src/scripts/_lib/read_surface_scan.ts +322 -0
- package/src/scripts/_lib/release_highlights.ts +201 -11
- package/src/scripts/_lib/release_holds.ts +493 -0
- package/src/scripts/_lib/release_migration_gate.ts +184 -0
- package/src/scripts/_lib/retrieval_sanitize.ts +77 -16
- package/src/scripts/_lib/rides_along.ts +102 -0
- package/src/scripts/_lib/settings_renamed_keys.ts +82 -0
- package/src/scripts/_lib/shell_write_shapes.ts +108 -0
- package/src/scripts/_lib/structural_hiding.ts +622 -0
- package/src/scripts/_lib/test_delta.ts +166 -0
- package/src/scripts/_lib/test_provenance.ts +171 -0
- package/src/scripts/_lib/turn_end_refusals.ts +525 -4
- package/src/scripts/_lib/turn_end_transcript.ts +216 -0
- package/src/scripts/_lib/typed_op_grant.ts +246 -0
- package/src/scripts/_lib/typed_op_watch.ts +185 -0
- package/src/scripts/_lib/verification_command.ts +324 -0
- package/src/scripts/_lib/verification_evidence.ts +647 -0
- package/src/scripts/_lib/verify_clause.ts +280 -0
- package/src/scripts/ai_council/config.ts +8 -11
- package/src/scripts/ai_council/fallback_config.ts +5 -3
- package/src/scripts/ai_council/ownership_classes.ts +118 -0
- package/src/scripts/ai_team/team_dispatch.ts +4 -0
- package/src/scripts/ask_block_census.ts +285 -15
- package/src/scripts/before_complete_hook.ts +340 -23
- package/src/scripts/bench_hook_latency.ts +412 -5
- package/src/scripts/cache_realization_report.ts +61 -1
- package/src/scripts/check_argument_hint.ts +287 -0
- package/src/scripts/check_claims.ts +125 -1
- package/src/scripts/check_enforcement_coverage.ts +38 -9
- package/src/scripts/check_enforcement_matrix.ts +866 -0
- package/src/scripts/check_finding_dispositions.ts +22 -3
- package/src/scripts/check_gate_reachability.ts +173 -10
- package/src/scripts/check_generator_sync.ts +1025 -0
- package/src/scripts/check_host_docs_digest.ts +786 -0
- package/src/scripts/check_host_format_column.ts +559 -0
- package/src/scripts/check_input_references.ts +228 -0
- package/src/scripts/check_inputs_parity.ts +235 -0
- package/src/scripts/check_kernel_edit_ratified.ts +47 -5
- package/src/scripts/check_kill_switch_table.ts +294 -0
- package/src/scripts/check_placeholder_drift.ts +221 -0
- package/src/scripts/check_preamble_payload_budget.ts +75 -2
- package/src/scripts/check_read_surface_coverage.ts +515 -0
- package/src/scripts/check_release_highlights.ts +33 -1
- package/src/scripts/check_release_holds.ts +568 -0
- package/src/scripts/check_routing_coverage.ts +378 -11
- package/src/scripts/check_rule_projection_integrity.ts +22 -12
- package/src/scripts/check_static_parity.ts +17 -0
- package/src/scripts/check_test_delta.ts +294 -0
- package/src/scripts/check_test_weakening.ts +299 -0
- package/src/scripts/ci_settle.ts +145 -19
- package/src/scripts/closure_scan.ts +447 -0
- package/src/scripts/compile_hook_manifest.ts +102 -18
- package/src/scripts/council_record_shape.ts +209 -0
- package/src/scripts/external_sources_denylist.json +29 -4
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +127 -11
- package/src/scripts/hooks/block_config_weakening.ts +229 -4
- package/src/scripts/hooks/block_kernel_rule_writes.ts +9 -78
- package/src/scripts/hooks/block_plumbing_writes.ts +314 -0
- package/src/scripts/hooks/bundle_integrity.ts +291 -0
- package/src/scripts/hooks/chain_nudge_hook.ts +1 -2
- package/src/scripts/hooks/code_graph_context_hook.ts +1 -2
- package/src/scripts/hooks/comment_discipline_hook.ts +1 -3
- package/src/scripts/hooks/concern_registry.ts +6 -0
- package/src/scripts/hooks/concern_timings.ts +94 -0
- package/src/scripts/hooks/delegation_nudge_hook.ts +1 -2
- package/src/scripts/hooks/design_pass_hook.ts +221 -5
- package/src/scripts/hooks/design_slop_hook.ts +1 -2
- package/src/scripts/hooks/dispatch_hook.ts +25 -19
- package/src/scripts/hooks/edit_shape_hook.ts +1 -2
- package/src/scripts/hooks/end_review_nudge_hook.ts +1 -1
- package/src/scripts/hooks/evidence_independence.ts +97 -2
- package/src/scripts/hooks/exit_codes.ts +128 -0
- package/src/scripts/hooks/host_lowering.json +1 -0
- package/src/scripts/hooks/host_lowering.ts +128 -3
- package/src/scripts/hooks/host_lowering.yaml +262 -46
- package/src/scripts/hooks/host_semantics.ts +30 -2
- package/src/scripts/hooks/injection_budget.ts +9 -2
- package/src/scripts/hooks/interruption_ledger_hook.ts +1 -2
- package/src/scripts/hooks/journal_record_hook.ts +1 -2
- package/src/scripts/hooks/memory_recall_hook.ts +539 -0
- package/src/scripts/hooks/obligation_settle_hook.ts +328 -0
- package/src/scripts/hooks/one_question_per_ask_hook.ts +25 -4
- package/src/scripts/hooks/orchestration_record_hook.ts +1 -2
- package/src/scripts/hooks/permission_gate.ts +1 -2
- package/src/scripts/hooks/push_settle_hook.ts +1 -3
- package/src/scripts/hooks/replay_hook.ts +2 -1
- package/src/scripts/hooks/reread_guard_hook.ts +1 -2
- package/src/scripts/hooks/review_baseline_hook.ts +1 -2
- package/src/scripts/hooks/rtk_wrap_hook.ts +1 -2
- package/src/scripts/hooks/rule_inject_hook.ts +55 -4
- package/src/scripts/hooks/run_continuation_hook.ts +30 -35
- package/src/scripts/hooks/skill_route_hook.ts +1 -2
- package/src/scripts/hooks/source_first_gate_hook.ts +1 -2
- package/src/scripts/hooks/spawn_guard_shadow_hook.ts +1 -2
- package/src/scripts/hooks/subagent_ledger_hook.ts +1 -2
- package/src/scripts/hooks/suggestion_capture_hook.ts +1 -1
- package/src/scripts/hooks/table_fingerprint.ts +49 -0
- package/src/scripts/hooks/telemetry_flush_hook.ts +1 -2
- package/src/scripts/hooks/telemetry_usage_hook.ts +1 -2
- package/src/scripts/hooks/tool_result_bytes_hook.ts +93 -9
- package/src/scripts/hooks/turn_end_gate_hook.ts +337 -324
- package/src/scripts/hooks/ui_route_nudge_hook.ts +1 -2
- package/src/scripts/hooks_doctor.ts +85 -0
- package/src/scripts/hooks_status.ts +19 -0
- package/src/scripts/install.ts +109 -210
- package/src/scripts/janitor.ts +7 -0
- package/src/scripts/lint_decision_classes.ts +464 -0
- package/src/scripts/lint_deny_text.ts +419 -0
- package/src/scripts/lint_exit_codes.ts +262 -0
- package/src/scripts/lint_hook_manifest.ts +66 -0
- package/src/scripts/lint_major_migration_sections.ts +450 -0
- package/src/scripts/lint_plan_risk_register.ts +47 -3
- package/src/scripts/lint_roadmap_blockers.ts +139 -12
- package/src/scripts/lint_roadmap_producers.ts +202 -0
- package/src/scripts/lint_skill_descriptions.ts +1 -1
- package/src/scripts/lint_skill_scripts_readonly.ts +1 -1
- package/src/scripts/mcp_server/prompts.ts +142 -1
- package/src/scripts/measure_rule_budget.ts +101 -5
- package/src/scripts/measure_skill_ranker_baseline.ts +305 -51
- package/src/scripts/measure_turn_end_gate.ts +193 -0
- package/src/scripts/memory_lookup.ts +1 -1
- package/src/scripts/new_roadmap.ts +37 -0
- package/src/scripts/orchestration_record.ts +0 -5
- package/src/scripts/prepack-check.mjs +53 -0
- package/src/scripts/release.ts +14 -98
- package/src/scripts/release_drill.ts +267 -1
- package/src/scripts/release_publication.ts +64 -1
- package/src/scripts/report_enforcement_drift.ts +566 -0
- package/src/scripts/report_host_injection_effect.ts +3 -11
- package/src/scripts/report_invocation_surface.ts +383 -0
- package/src/scripts/report_obligation_carriers.ts +699 -0
- package/src/scripts/report_obligation_writer_split.ts +236 -0
- package/src/scripts/report_skill_menu_census.ts +208 -0
- package/src/scripts/report_standing_payload_by_host.ts +26 -152
- package/src/scripts/roadmap_verify_share.ts +213 -0
- package/src/scripts/schemas/agent-settings.schema.json +23 -0
- package/src/scripts/schemas/command.schema.json +48 -0
- package/src/scripts/schemas/review-findings.schema.json +4 -0
- package/src/scripts/schemas/skill.schema.json +44 -0
- package/src/scripts/scope_growth.ts +219 -0
- package/src/scripts/self_review_gate.ts +359 -10
- package/src/scripts/semantic_parity_census.ts +314 -0
- package/src/scripts/session_eol_report.ts +54 -1
- package/src/scripts/sweep_dead_scan_roots.ts +1 -1
- package/src/scripts/trigger_eval_grandfather.json +0 -3
- package/src/scripts/trigger_eval_rotation.ts +122 -27
- package/src/scripts/ui_conformance_probe.ts +774 -0
- package/src/scripts/update_prices.ts +85 -3
- package/src/scripts/write_bundle_digest.mjs +74 -0
- package/src/server/schemas/settings.ts +62 -7
- package/src/shared/missionExecution.ts +126 -0
- package/src/templates/consumer-settings/ONBOARDING.md +1 -0
- package/dist/cli-delegate/chunk-523HCFYO.js +0 -90
- package/dist/ui/assets/index-D0jaCRPM.js +0 -29
- package/dist/ui/assets/index-jSfMW8_t.css +0 -1
- /package/dist/cli-delegate/{chunk-YUM5KURG.js → chunk-7RQP4UDH.js} +0 -0
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The obligation ledger — write side.
|
|
3
|
+
*
|
|
4
|
+
* This repository already knows which obligations it delivered into a turn.
|
|
5
|
+
* `hooks/rule_inject_hook.ts` writes one record per session naming every rule
|
|
6
|
+
* whose body it injected, and until now the only reads of that path were the
|
|
7
|
+
* writer itself and its own test. The tree could say what it delivered and
|
|
8
|
+
* nothing could ask whether the turn discharged any of it.
|
|
9
|
+
*
|
|
10
|
+
* This module is the state that closes that gap, and ONLY the state. It adds
|
|
11
|
+
* no reader, no detector and no refusal — those arrive in later phases of
|
|
12
|
+
* `road-to-a-ledger-that-closes-the-loop`, deliberately after the rows exist,
|
|
13
|
+
* so that a detector is written against real recorded shapes instead of
|
|
14
|
+
* against an imagined one.
|
|
15
|
+
*
|
|
16
|
+
* WHAT A `delivered` ROW MEANS, AND WHAT IT MAY NEVER BE CITED FOR.
|
|
17
|
+
* A row records that an EMITTER RAN. It does not record that a rule reached
|
|
18
|
+
* the model, and it may not be cited as evidence that one did. That question
|
|
19
|
+
* is council-locked as blocked-by-architecture (2026-08-31, two convergent
|
|
20
|
+
* seats) on a reproducible probe: an obligation present in the shipped tree
|
|
21
|
+
* was absent from the operator's installed copy, so installation proves
|
|
22
|
+
* availability and not exposure. The lock tested a different mechanism than
|
|
23
|
+
* this one, which is why the write side is allowed at all — but a reader who
|
|
24
|
+
* turns these rows into a compliance claim has made exactly the assertion the
|
|
25
|
+
* council refused.
|
|
26
|
+
*
|
|
27
|
+
* WHY THE PATH COMES FROM A HELPER AND NOT A LITERAL.
|
|
28
|
+
* `statePathFor` below is the same shape as `before_complete_hook.ts:128` —
|
|
29
|
+
* `session_state_file(<dir>, session_id)` — and shares that function rather
|
|
30
|
+
* than reimplementing it. The digest keying matters: it removes the
|
|
31
|
+
* filename-length failure mode for long ids, and it is the property an
|
|
32
|
+
* in-file `session_id` integrity check is written against. A second
|
|
33
|
+
* hand-rolled sanitiser here would be a second shape, and two shapes for one
|
|
34
|
+
* concept is how a session's state becomes unfindable by the tool meant to
|
|
35
|
+
* read it.
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
import fs from 'node:fs';
|
|
39
|
+
import path from 'node:path';
|
|
40
|
+
|
|
41
|
+
import {
|
|
42
|
+
atomic_write_json,
|
|
43
|
+
is_replay_mode,
|
|
44
|
+
session_state_file,
|
|
45
|
+
update_json_under_lock,
|
|
46
|
+
} from '../hooks/state_io.js';
|
|
47
|
+
import { manifest_path } from './installed_tools.js';
|
|
48
|
+
import { type EnforcementClass, is_enforcement_class } from './obligation_frequency.js';
|
|
49
|
+
import { hasSentinel } from './repo_root.js';
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Directory holding one ledger file per session.
|
|
53
|
+
*
|
|
54
|
+
* Under `agents/runtime/state/`, which is gitignored and janitor-pruned, and
|
|
55
|
+
* which carries a `disposition: excluded` row in
|
|
56
|
+
* `src/config/continuity-surface.json` — this is per-session instrumentation,
|
|
57
|
+
* not continuity state a later session must be able to resume from.
|
|
58
|
+
*/
|
|
59
|
+
export const OBLIGATION_STATE_DIR = path.join('agents', 'runtime', 'state', 'obligations');
|
|
60
|
+
|
|
61
|
+
/** Path of one session's ledger. Same shape as the existing consumer. */
|
|
62
|
+
export function statePathFor(session_id: string): string {
|
|
63
|
+
return session_state_file(OBLIGATION_STATE_DIR, session_id);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* WHICH KIND OF TREE WROTE A ROW — the whole vocabulary, and nothing else.
|
|
68
|
+
*
|
|
69
|
+
* `obligation-settle-shadow-bar` is read off `agents/runtime/state/obligations/`,
|
|
70
|
+
* which is one machine's gitignored runtime state. That scope bound lives in the
|
|
71
|
+
* claim's prose and in a roadmap paragraph, and in nothing a reader of the rows
|
|
72
|
+
* can compute. A reading that clears the pre-registered floor would therefore be
|
|
73
|
+
* quotable as evidence about a population nobody separated. This field makes the
|
|
74
|
+
* separation a property of the DATA, so the split is computable from the ledger
|
|
75
|
+
* alone rather than asserted beside it.
|
|
76
|
+
*
|
|
77
|
+
* THREE VALUES, CLOSED, AND A UNION OF LITERALS ON PURPOSE. The record gains one
|
|
78
|
+
* enumerated field and no `payload`, `notes` or `extra` slot, so the privacy
|
|
79
|
+
* property is the SHAPE and not a scrubber somebody has to keep correct — the
|
|
80
|
+
* same by-construction posture `artifact-engagement-recording` states for the
|
|
81
|
+
* telemetry event. A type that cannot hold a path cannot leak one.
|
|
82
|
+
*
|
|
83
|
+
* WHAT IT DELIBERATELY IS NOT. Not a path, not a username, not a hostname, not a
|
|
84
|
+
* machine id, not a git remote. The obvious implementation of "which tree wrote
|
|
85
|
+
* this" is the resolved root's filesystem path, which on a developer machine
|
|
86
|
+
* carries an account name — and because the bar is read by QUOTING rows, one such
|
|
87
|
+
* field would make every row already written unquotable rather than scrubbable.
|
|
88
|
+
* So the field carries a ROLE: what kind of tree this is, never which one.
|
|
89
|
+
*/
|
|
90
|
+
export type WriterRole =
|
|
91
|
+
/** The tree carries this package's own `package.json` sentinel — a maintainer checkout. */
|
|
92
|
+
| 'package'
|
|
93
|
+
/** Not the package, but an install manifest is present — a consumer project. */
|
|
94
|
+
| 'consumer'
|
|
95
|
+
/** Neither marker resolved. An honest absence, never a default. */
|
|
96
|
+
| 'unknown';
|
|
97
|
+
|
|
98
|
+
/** The closed set, in one place, so a reader and a writer cannot disagree. */
|
|
99
|
+
export const WRITER_ROLES: readonly WriterRole[] = ['package', 'consumer', 'unknown'];
|
|
100
|
+
|
|
101
|
+
/** Narrowing guard for a value read back off disk. */
|
|
102
|
+
export function isWriterRole(value: unknown): value is WriterRole {
|
|
103
|
+
return typeof value === 'string' && (WRITER_ROLES as readonly string[]).includes(value);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Classify the root this ledger lives under.
|
|
108
|
+
*
|
|
109
|
+
* ORDER IS THE CONTRACT. `package` is tested FIRST, because a maintainer checkout
|
|
110
|
+
* routinely also carries the install artifacts a consumer has — this repository
|
|
111
|
+
* installs itself — and a consumer-first test would label the maintainer tree
|
|
112
|
+
* `consumer` on exactly the machine the bar is being read from. The sentinel is
|
|
113
|
+
* `hasSentinel` from `repo_root.ts` rather than a second `package.json` reader:
|
|
114
|
+
* the sentinel name lives in one place or it drifts.
|
|
115
|
+
*
|
|
116
|
+
* `consumer` keys on the INSTALL MANIFEST (`agents/installed-tools.lock`, via the
|
|
117
|
+
* canonical `manifest_path` so the `AGENT_CONFIG_INSTALLED_TOOLS` override is
|
|
118
|
+
* honoured) and not on `.agent-settings.yml`. The settings file is something a
|
|
119
|
+
* human writes by hand; the lock is written by the installer and by nothing else.
|
|
120
|
+
* A weaker marker would buy coverage by guessing, which is the one thing this
|
|
121
|
+
* field may not do.
|
|
122
|
+
*
|
|
123
|
+
* NEVER THROWS. A probe that cannot stat returns `unknown`, which is a real
|
|
124
|
+
* answer — the ledger's contract is that an instrument missing a reading is
|
|
125
|
+
* cheaper than a refused turn, and a classifier is no exception.
|
|
126
|
+
*/
|
|
127
|
+
export function resolveWriterRole(root: string): WriterRole {
|
|
128
|
+
try {
|
|
129
|
+
if (hasSentinel(root)) return 'package';
|
|
130
|
+
if (fs.existsSync(manifest_path(root))) return 'consumer';
|
|
131
|
+
} catch {
|
|
132
|
+
return 'unknown';
|
|
133
|
+
}
|
|
134
|
+
return 'unknown';
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* One rule delivered into this session, with the class its frontmatter declares.
|
|
139
|
+
*
|
|
140
|
+
* `cls` rather than `class` because `class` is a reserved word, and a field
|
|
141
|
+
* named around a keyword is worse than a field named honestly. The serialised
|
|
142
|
+
* key is `class`, which is what a reader of the JSON expects to see.
|
|
143
|
+
*/
|
|
144
|
+
export interface DeliveredRow {
|
|
145
|
+
/** Rule id, as the injector names it. */
|
|
146
|
+
readonly rule: string;
|
|
147
|
+
/** Declared enforcement class, or `none` when the rule declares none. */
|
|
148
|
+
readonly cls: EnforcementClass;
|
|
149
|
+
/** ISO-8601, second precision. */
|
|
150
|
+
readonly at: string;
|
|
151
|
+
/** Which kind of tree wrote this. Stamped here, never supplied — see {@link WriterInput}. */
|
|
152
|
+
readonly writer: WriterRole;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* What a CALLER offers, for every row type: the stored shape minus its writer.
|
|
157
|
+
*
|
|
158
|
+
* The writer is stamped by the append functions from the `root` they were
|
|
159
|
+
* already given, and is deliberately not part of any caller's input. A caller
|
|
160
|
+
* that could supply it could supply the wrong one — and a provenance field a
|
|
161
|
+
* caller can set is a provenance field, not provenance. Making it unsettable is
|
|
162
|
+
* cheaper than any check that it was set correctly.
|
|
163
|
+
*/
|
|
164
|
+
export type WriterInput<T> = Omit<T, 'writer'>;
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* One obligation discharged this session.
|
|
168
|
+
*
|
|
169
|
+
* Written by the concern that ALREADY made the decision — the design-pass
|
|
170
|
+
* concern knows the UI audit is fresh, so it is the only thing that can say so
|
|
171
|
+
* without re-deriving a verdict it does not own. A discharge nobody observed
|
|
172
|
+
* is never inferred: absence of a row means nothing was recorded, not that
|
|
173
|
+
* nothing happened, and the shadow rows below are labelled accordingly.
|
|
174
|
+
*/
|
|
175
|
+
export interface DischargeRow {
|
|
176
|
+
/** Rule id whose obligation this discharges. */
|
|
177
|
+
readonly rule: string;
|
|
178
|
+
/** What observed it — a concern name, so a reader can go and check. */
|
|
179
|
+
readonly by: string;
|
|
180
|
+
readonly at: string;
|
|
181
|
+
/** Which kind of tree wrote this. Stamped, never supplied. */
|
|
182
|
+
readonly writer: WriterRole;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* One turn-end reading that WOULD have refused, had anything been armed.
|
|
187
|
+
*
|
|
188
|
+
* The whole point of the shadow phase: the detector runs, the verdict is
|
|
189
|
+
* recorded, and nothing is refused. A row here is evidence about the
|
|
190
|
+
* detector's behaviour, never about the turn's compliance.
|
|
191
|
+
*/
|
|
192
|
+
export interface ShadowRow {
|
|
193
|
+
readonly at: string;
|
|
194
|
+
/** The missing SET, as one row. Never one row per obligation — see `appendShadow`. */
|
|
195
|
+
readonly missing: readonly string[];
|
|
196
|
+
/** 1 for the first reading of a given missing set, incrementing after. */
|
|
197
|
+
readonly attempt: number;
|
|
198
|
+
/** Always true today: a row is only written when the detector would refuse. */
|
|
199
|
+
readonly would_refuse: boolean;
|
|
200
|
+
/**
|
|
201
|
+
* Which kind of tree wrote this. Stamped, never supplied.
|
|
202
|
+
*
|
|
203
|
+
* The shadow array is the one the pre-registered bar actually counts, so
|
|
204
|
+
* this is the field that turns "the corpus is one machine" from a sentence
|
|
205
|
+
* in a claim into something a reader can compute off the rows.
|
|
206
|
+
*/
|
|
207
|
+
readonly writer: WriterRole;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** The on-disk shape of one session's ledger. */
|
|
211
|
+
export interface ObligationLedger {
|
|
212
|
+
/** Cheap integrity check against a copied or hand-edited file. */
|
|
213
|
+
readonly session_id: string;
|
|
214
|
+
readonly delivered: readonly DeliveredRow[];
|
|
215
|
+
readonly discharged: readonly DischargeRow[];
|
|
216
|
+
readonly shadow: readonly ShadowRow[];
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/** The JSON spelling of a row — `class` on disk, `cls` in TypeScript. */
|
|
220
|
+
interface SerialisedRow {
|
|
221
|
+
rule: string;
|
|
222
|
+
class: string;
|
|
223
|
+
at: string;
|
|
224
|
+
writer: WriterRole;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function serialise(row: DeliveredRow): SerialisedRow {
|
|
228
|
+
return { rule: row.rule, class: row.cls, at: row.at, writer: row.writer };
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Read rows back out of whatever is on disk, discarding anything malformed.
|
|
233
|
+
*
|
|
234
|
+
* Deliberately lenient about the FILE and strict about the ROW. A truncated or
|
|
235
|
+
* hand-edited ledger must not cost the rows that are still intact, and a row
|
|
236
|
+
* whose class is outside the closed set must not enter the ledger wearing a
|
|
237
|
+
* value the vocabulary denies — `is_enforcement_class` is the gate, so a
|
|
238
|
+
* future widening of the schema cannot leak in through the state file.
|
|
239
|
+
*
|
|
240
|
+
* THE WRITER IS TREATED THE OTHER WAY ROUND, and the asymmetry is deliberate. A
|
|
241
|
+
* row whose CLASS is outside the vocabulary is dropped, because the class is the
|
|
242
|
+
* row's subject matter and a row wearing a denied class is a false statement
|
|
243
|
+
* about what was delivered. A row whose WRITER is absent or unrecognised is KEPT
|
|
244
|
+
* and reads `unknown`, because the writer is provenance ABOUT a delivery that
|
|
245
|
+
* genuinely happened — dropping the row would destroy a real record to punish a
|
|
246
|
+
* missing label, and every row written before this field existed is exactly that
|
|
247
|
+
* case. `unknown` is the honest reading of both, which is why one value covers
|
|
248
|
+
* absent and unresolvable alike.
|
|
249
|
+
*/
|
|
250
|
+
export function parseRows(loaded: unknown): DeliveredRow[] {
|
|
251
|
+
if (typeof loaded !== 'object' || loaded === null) return [];
|
|
252
|
+
const raw = (loaded as { delivered?: unknown }).delivered;
|
|
253
|
+
if (!Array.isArray(raw)) return [];
|
|
254
|
+
const out: DeliveredRow[] = [];
|
|
255
|
+
for (const entry of raw) {
|
|
256
|
+
if (typeof entry !== 'object' || entry === null) continue;
|
|
257
|
+
const e = entry as Record<string, unknown>;
|
|
258
|
+
const rule = e['rule'];
|
|
259
|
+
const cls = e['class'];
|
|
260
|
+
const at = e['at'];
|
|
261
|
+
if (typeof rule !== 'string' || rule === '') continue;
|
|
262
|
+
if (typeof at !== 'string' || at === '') continue;
|
|
263
|
+
if (!is_enforcement_class(cls)) continue;
|
|
264
|
+
const writer = e['writer'];
|
|
265
|
+
out.push({ rule, cls, at, writer: isWriterRole(writer) ? writer : 'unknown' });
|
|
266
|
+
}
|
|
267
|
+
return out;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/** ISO-8601 to second precision — the ledger has no use for milliseconds. */
|
|
271
|
+
export function stamp(now: Date = new Date()): string {
|
|
272
|
+
return `${now.toISOString().slice(0, 19)}Z`;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Append delivered rows for one session, idempotently per rule.
|
|
277
|
+
*
|
|
278
|
+
* Idempotent because the injector's own contract is once-per-session-per-rule
|
|
279
|
+
* re-armed on compaction: after a compaction a rule is legitimately delivered
|
|
280
|
+
* again, and a second row would say the same thing twice while a reader
|
|
281
|
+
* counting distinct obligations wants it once. First delivery wins, so the
|
|
282
|
+
* timestamp is the one that answers "when did this session first come under
|
|
283
|
+
* this rule".
|
|
284
|
+
*
|
|
285
|
+
* Returns the number of rows actually added — 0 is a real answer (every rule
|
|
286
|
+
* already recorded) and is not a failure.
|
|
287
|
+
*
|
|
288
|
+
* NEVER THROWS, and never fails a turn. A ledger that cannot be written is an
|
|
289
|
+
* instrument that missed a reading; a turn refused because an instrument
|
|
290
|
+
* missed a reading is a worse outcome than the missing reading, and this
|
|
291
|
+
* module has no refusal to offer in the first place.
|
|
292
|
+
*/
|
|
293
|
+
export function appendDelivered(
|
|
294
|
+
root: string,
|
|
295
|
+
session_id: string,
|
|
296
|
+
rows: readonly WriterInput<DeliveredRow>[],
|
|
297
|
+
): number {
|
|
298
|
+
if (rows.length === 0) return 0;
|
|
299
|
+
if (is_replay_mode()) return 0;
|
|
300
|
+
if (session_id.trim() === '') return 0;
|
|
301
|
+
|
|
302
|
+
const target = path.join(root, statePathFor(session_id));
|
|
303
|
+
const writer = resolveWriterRole(root);
|
|
304
|
+
let added = 0;
|
|
305
|
+
try {
|
|
306
|
+
update_json_under_lock<LedgerFile>(target, (loaded) => {
|
|
307
|
+
const existing = parseRows(loaded);
|
|
308
|
+
const seen = new Set(existing.map((r) => r.rule));
|
|
309
|
+
const merged = existing.map(serialise);
|
|
310
|
+
for (const row of rows) {
|
|
311
|
+
if (seen.has(row.rule)) continue;
|
|
312
|
+
seen.add(row.rule);
|
|
313
|
+
merged.push(serialise({ ...row, writer }));
|
|
314
|
+
added += 1;
|
|
315
|
+
}
|
|
316
|
+
if (added === 0) return null; // deliberate no-write
|
|
317
|
+
return { ...carryOver(loaded), session_id, delivered: merged };
|
|
318
|
+
});
|
|
319
|
+
} catch {
|
|
320
|
+
return 0;
|
|
321
|
+
}
|
|
322
|
+
return added;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/** The on-disk file, as the lock's mutator sees it. */
|
|
326
|
+
interface LedgerFile {
|
|
327
|
+
session_id: string;
|
|
328
|
+
delivered: SerialisedRow[];
|
|
329
|
+
discharged: DischargeRow[];
|
|
330
|
+
shadow: ShadowRow[];
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* The arrays this write is not touching, preserved verbatim.
|
|
335
|
+
*
|
|
336
|
+
* Three writers share one file and each owns one array. Without this, the
|
|
337
|
+
* last writer of a turn silently truncates the other two — the kind of loss
|
|
338
|
+
* that shows up as a detector reporting a clean turn because the discharges
|
|
339
|
+
* it was meant to read were dropped by the delivery write that followed them.
|
|
340
|
+
*/
|
|
341
|
+
function carryOver(loaded: Partial<LedgerFile>): Pick<LedgerFile, 'discharged' | 'shadow'> {
|
|
342
|
+
return {
|
|
343
|
+
discharged: Array.isArray(loaded.discharged) ? loaded.discharged : [],
|
|
344
|
+
shadow: Array.isArray(loaded.shadow) ? loaded.shadow : [],
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Record that one concern observed an obligation discharged.
|
|
350
|
+
*
|
|
351
|
+
* Idempotent per (rule, by): the design-pass concern may fire many times in a
|
|
352
|
+
* turn and the ledger wants the fact, not the frequency.
|
|
353
|
+
*/
|
|
354
|
+
export function appendDischarge(
|
|
355
|
+
root: string,
|
|
356
|
+
session_id: string,
|
|
357
|
+
rows: readonly WriterInput<DischargeRow>[],
|
|
358
|
+
): number {
|
|
359
|
+
if (rows.length === 0) return 0;
|
|
360
|
+
if (is_replay_mode()) return 0;
|
|
361
|
+
if (session_id.trim() === '') return 0;
|
|
362
|
+
|
|
363
|
+
const target = path.join(root, statePathFor(session_id));
|
|
364
|
+
const writer = resolveWriterRole(root);
|
|
365
|
+
let added = 0;
|
|
366
|
+
try {
|
|
367
|
+
update_json_under_lock<LedgerFile>(target, (loaded) => {
|
|
368
|
+
const existing = Array.isArray(loaded.discharged) ? loaded.discharged : [];
|
|
369
|
+
const key = (r: WriterInput<DischargeRow>): string => `${r.rule}::${r.by}`;
|
|
370
|
+
const seen = new Set(existing.map(key));
|
|
371
|
+
const merged = [...existing];
|
|
372
|
+
for (const row of rows) {
|
|
373
|
+
if (seen.has(key(row))) continue;
|
|
374
|
+
seen.add(key(row));
|
|
375
|
+
merged.push({ ...row, writer });
|
|
376
|
+
added += 1;
|
|
377
|
+
}
|
|
378
|
+
if (added === 0) return null;
|
|
379
|
+
return {
|
|
380
|
+
session_id,
|
|
381
|
+
delivered: parseRows(loaded).map(serialise),
|
|
382
|
+
discharged: merged,
|
|
383
|
+
shadow: Array.isArray(loaded.shadow) ? loaded.shadow : [],
|
|
384
|
+
};
|
|
385
|
+
});
|
|
386
|
+
} catch {
|
|
387
|
+
return 0;
|
|
388
|
+
}
|
|
389
|
+
return added;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/** Rules discharged this session, by rule id. */
|
|
393
|
+
export function readDischarged(root: string, session_id: string): DischargeRow[] {
|
|
394
|
+
const file = readLedgerFile(root, session_id);
|
|
395
|
+
if (file === null) return [];
|
|
396
|
+
return Array.isArray(file.discharged) ? file.discharged : [];
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/** Shadow readings recorded this session. */
|
|
400
|
+
export function readShadow(root: string, session_id: string): ShadowRow[] {
|
|
401
|
+
const file = readLedgerFile(root, session_id);
|
|
402
|
+
if (file === null) return [];
|
|
403
|
+
return Array.isArray(file.shadow) ? file.shadow : [];
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Record one would-refuse reading — ONE ROW PER MISSING SET, never per obligation.
|
|
408
|
+
*
|
|
409
|
+
* The aggregation is the contract, not an optimisation. Five missing
|
|
410
|
+
* obligations are one reading of one turn, and emitting five rows would let a
|
|
411
|
+
* later count of "refusals" read five times the truth while every one of them
|
|
412
|
+
* described the same moment.
|
|
413
|
+
*
|
|
414
|
+
* `attempt` increments when the SAME missing set is seen again. An unchanged
|
|
415
|
+
* set on a second reading is the signal that nothing moved — the caller uses
|
|
416
|
+
* it to stop forcing continuation, and it is recorded here so that decision is
|
|
417
|
+
* auditable rather than re-derived.
|
|
418
|
+
*
|
|
419
|
+
* Returns the attempt number written, or 0 when nothing was written.
|
|
420
|
+
*/
|
|
421
|
+
export function appendShadow(
|
|
422
|
+
root: string,
|
|
423
|
+
session_id: string,
|
|
424
|
+
missing: readonly string[],
|
|
425
|
+
now: string = stamp(),
|
|
426
|
+
): number {
|
|
427
|
+
if (missing.length === 0) return 0;
|
|
428
|
+
if (is_replay_mode()) return 0;
|
|
429
|
+
if (session_id.trim() === '') return 0;
|
|
430
|
+
|
|
431
|
+
const fingerprint = [...missing].sort().join('::');
|
|
432
|
+
const target = path.join(root, statePathFor(session_id));
|
|
433
|
+
const writer = resolveWriterRole(root);
|
|
434
|
+
let attempt = 0;
|
|
435
|
+
try {
|
|
436
|
+
update_json_under_lock<LedgerFile>(target, (loaded) => {
|
|
437
|
+
const existing = Array.isArray(loaded.shadow) ? loaded.shadow : [];
|
|
438
|
+
const prior = existing.filter(
|
|
439
|
+
(r) => [...(r.missing ?? [])].sort().join('::') === fingerprint,
|
|
440
|
+
);
|
|
441
|
+
attempt = prior.length + 1;
|
|
442
|
+
return {
|
|
443
|
+
session_id,
|
|
444
|
+
delivered: parseRows(loaded).map(serialise),
|
|
445
|
+
discharged: Array.isArray(loaded.discharged) ? loaded.discharged : [],
|
|
446
|
+
shadow: [
|
|
447
|
+
...existing,
|
|
448
|
+
{ at: now, missing: [...missing], attempt, would_refuse: true, writer },
|
|
449
|
+
],
|
|
450
|
+
};
|
|
451
|
+
});
|
|
452
|
+
} catch {
|
|
453
|
+
return 0;
|
|
454
|
+
}
|
|
455
|
+
return attempt;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/** The whole file for one session, or `null` when it is absent or not ours. */
|
|
459
|
+
function readLedgerFile(root: string, session_id: string): Partial<LedgerFile> | null {
|
|
460
|
+
if (session_id.trim() === '') return null;
|
|
461
|
+
const target = path.join(root, statePathFor(session_id));
|
|
462
|
+
try {
|
|
463
|
+
const decoded: unknown = JSON.parse(fs.readFileSync(target, 'utf-8'));
|
|
464
|
+
if (typeof decoded !== 'object' || decoded === null) return null;
|
|
465
|
+
if ((decoded as { session_id?: unknown }).session_id !== session_id) return null;
|
|
466
|
+
return decoded as Partial<LedgerFile>;
|
|
467
|
+
} catch {
|
|
468
|
+
return null;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Rows recorded for one session, or `[]` when there are none to read.
|
|
474
|
+
*
|
|
475
|
+
* A file carrying somebody else's `session_id` reads as empty rather than as
|
|
476
|
+
* that session's rows — a copy, a restore or a hand-edit must not be able to
|
|
477
|
+
* attribute one session's deliveries to another. The producer side repairs
|
|
478
|
+
* the same case by rewriting the id; the consumer side refuses it. Both halves
|
|
479
|
+
* are needed, as `hooks/state_io.ts` § Per-session concern state states for
|
|
480
|
+
* the two concerns that got here first.
|
|
481
|
+
*/
|
|
482
|
+
export function readDelivered(root: string, session_id: string): DeliveredRow[] {
|
|
483
|
+
if (session_id.trim() === '') return [];
|
|
484
|
+
const target = path.join(root, statePathFor(session_id));
|
|
485
|
+
try {
|
|
486
|
+
const decoded: unknown = JSON.parse(fs.readFileSync(target, 'utf-8'));
|
|
487
|
+
if (typeof decoded !== 'object' || decoded === null) return [];
|
|
488
|
+
if ((decoded as { session_id?: unknown }).session_id !== session_id) return [];
|
|
489
|
+
return parseRows(decoded);
|
|
490
|
+
} catch {
|
|
491
|
+
return [];
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Whether the ledger directory can be written — the doctor's readiness line.
|
|
497
|
+
*
|
|
498
|
+
* Probes by writing and removing a sentinel rather than by checking mode bits,
|
|
499
|
+
* because the modes that matter here (a read-only mount, a directory owned by
|
|
500
|
+
* another user, a full disk) are not all visible in a `stat`. Returns `false`
|
|
501
|
+
* rather than throwing: the doctor reports, it does not fail.
|
|
502
|
+
*/
|
|
503
|
+
export function ledgerWritable(root: string): boolean {
|
|
504
|
+
if (is_replay_mode()) return false;
|
|
505
|
+
const probe = path.join(root, OBLIGATION_STATE_DIR, '.writable-probe.json');
|
|
506
|
+
try {
|
|
507
|
+
atomic_write_json(probe, { probe: true });
|
|
508
|
+
fs.rmSync(probe, { force: true });
|
|
509
|
+
return true;
|
|
510
|
+
} catch {
|
|
511
|
+
return false;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
@@ -59,8 +59,6 @@ export type LinePhase = 'refine' | 'memory' | 'analyze' | 'plan' | 'implement' |
|
|
|
59
59
|
* table can be checked against it (`road-to-experience-loop-broadening` 1.3).
|
|
60
60
|
*/
|
|
61
61
|
export type LineOutcome = PhaseOutcome;
|
|
62
|
-
/** Which capsule-emission trigger arm fired first (Phase 1 shadow comparison). */
|
|
63
|
-
export type TriggerArm = 'watermark' | 'saturation' | 'tie';
|
|
64
62
|
/** The orchestration form the form-gate selected (road-to-opt-subagent-harvest P2). */
|
|
65
63
|
export type DispatchModeId =
|
|
66
64
|
| 'do-and-judge'
|
|
@@ -225,12 +223,16 @@ export interface RecordInput {
|
|
|
225
223
|
capsule_emitted?: boolean | undefined;
|
|
226
224
|
/** Total entries across the capsule's arrays — a size proxy, never content. */
|
|
227
225
|
capsule_entries?: number | null | undefined;
|
|
228
|
-
|
|
229
|
-
watermark_step
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
226
|
+
// The three trigger-comparison fields that used to sit here —
|
|
227
|
+
// `watermark_step`, `saturation_step`, `trigger_arm_earlier` — were removed
|
|
228
|
+
// on 2026-09-13. They were accepted by the CLI and no caller could ever
|
|
229
|
+
// compute them: `capsule_trigger.compareTriggers` needs per-step token
|
|
230
|
+
// counts AND per-step surfaced terms, and no payload this tree has observed
|
|
231
|
+
// carries either. `subagent_ledger_hook.ts` names that gap and refuses to
|
|
232
|
+
// invent a proxy, citing this exact module. A flag nothing can fill is the
|
|
233
|
+
// same built-and-unreachable class the rest of this change detects, so it
|
|
234
|
+
// goes rather than waiting behind a label. Re-adding them is one commit
|
|
235
|
+
// once an observation source exists.
|
|
234
236
|
// Audit-log envelope (sensible defaults for a dispatch record)
|
|
235
237
|
phase?: LinePhase | undefined;
|
|
236
238
|
outcome?: LineOutcome | undefined;
|
|
@@ -258,7 +260,6 @@ const BANDS: readonly Band[] = ['low', 'medium', 'high'];
|
|
|
258
260
|
const PHASES: readonly LinePhase[] = ['refine', 'memory', 'analyze', 'plan', 'implement', 'test', 'verify', 'report'];
|
|
259
261
|
const LOOKUP_CLASSES: readonly LookupClass[] = ['definition', 'references', 'string-existence', 'report-run'];
|
|
260
262
|
const ROUTES_TAKEN: readonly RouteTaken[] = ['primitive', 'subagent', 'ask'];
|
|
261
|
-
const TRIGGER_ARMS: readonly TriggerArm[] = ['watermark', 'saturation', 'tie'];
|
|
262
263
|
/** Hex-only payload hash — a hash can never smuggle content (privacy by construction). */
|
|
263
264
|
const PAYLOAD_HASH_RE = /^[a-f0-9]{8,64}$/i;
|
|
264
265
|
/** Id-shaped origin tag — enum-ish, never free-form prose. */
|
|
@@ -549,16 +550,9 @@ export function buildOrchestrationLine(input: RecordInput): BuiltLine {
|
|
|
549
550
|
if (input.capsule_emitted !== undefined && typeof input.capsule_emitted !== 'boolean') {
|
|
550
551
|
errors.push('capsule_emitted must be a boolean or omitted');
|
|
551
552
|
}
|
|
552
|
-
for (const [key, v] of [
|
|
553
|
-
['capsule_entries', input.capsule_entries],
|
|
554
|
-
['watermark_step', input.watermark_step],
|
|
555
|
-
['saturation_step', input.saturation_step],
|
|
556
|
-
] as const) {
|
|
553
|
+
for (const [key, v] of [['capsule_entries', input.capsule_entries]] as const) {
|
|
557
554
|
if (v != null && (!isInt(v) || v < 0)) errors.push(`${key} must be a non-negative integer count`);
|
|
558
555
|
}
|
|
559
|
-
if (input.trigger_arm_earlier != null && !TRIGGER_ARMS.includes(input.trigger_arm_earlier)) {
|
|
560
|
-
errors.push(`trigger_arm_earlier must be one of ${TRIGGER_ARMS.join(' | ')} (or omitted / null when neither arm fired)`);
|
|
561
|
-
}
|
|
562
556
|
|
|
563
557
|
for (const [key, v] of [
|
|
564
558
|
['model_requested', input.model_requested],
|
|
@@ -619,9 +613,6 @@ export function buildOrchestrationLine(input: RecordInput): BuiltLine {
|
|
|
619
613
|
// capsule shadow-measurement — readers ignore unknowns per audit-log-v1
|
|
620
614
|
capsule_emitted: input.capsule_emitted ?? null,
|
|
621
615
|
capsule_entries: input.capsule_entries ?? null,
|
|
622
|
-
watermark_step: input.watermark_step ?? null,
|
|
623
|
-
saturation_step: input.saturation_step ?? null,
|
|
624
|
-
trigger_arm_earlier: input.trigger_arm_earlier ?? null,
|
|
625
616
|
};
|
|
626
617
|
|
|
627
618
|
const line: Record<string, unknown> = {
|