@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,236 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* report_obligation_writer_split — which kind of tree wrote the obligation rows.
|
|
4
|
+
*
|
|
5
|
+
* WHAT QUESTION THIS ANSWERS, AND WHY IT NEEDED A TOOL.
|
|
6
|
+
* `obligation-settle-shadow-bar` is pre-registered against the shadow rows under
|
|
7
|
+
* `agents/runtime/state/obligations/`, and its own clause (7) says that corpus is
|
|
8
|
+
* one machine's runtime state. Until the rows carried a writer, that sentence was
|
|
9
|
+
* the ONLY place the fact lived: a reader holding the ledgers could count rows and
|
|
10
|
+
* could not separate them, so a reading that cleared the floor would have been
|
|
11
|
+
* quotable as evidence about a population nobody had measured. This prints the
|
|
12
|
+
* separation, from the rows themselves.
|
|
13
|
+
*
|
|
14
|
+
* IT REACHES NO VERDICT, AND THAT IS THE DESIGN. A split is an input to the corpus
|
|
15
|
+
* decision held in `road-to-an-obligation-row-that-names-its-writer`, not the
|
|
16
|
+
* decision — the three options there (accept the one-machine corpus and amend the
|
|
17
|
+
* bar, widen it deliberately, or file the window `resolved-null`) are owner-
|
|
18
|
+
* reserved, and all three name arming as owner-reserved too. So this exits 0 on
|
|
19
|
+
* every path, prints counts, and says nothing about whether anything may be armed.
|
|
20
|
+
* A reporter that graded the corpus would be handing the arming discussion a
|
|
21
|
+
* verdict wearing a number's clothes.
|
|
22
|
+
*
|
|
23
|
+
* `absent` IS REPORTED APART FROM `unknown`, and the distinction is load-bearing.
|
|
24
|
+
* `unknown` is a row whose writer WAS resolved and came back unresolvable; `absent`
|
|
25
|
+
* is a row written before the field existed. Folding them together would let the
|
|
26
|
+
* whole pre-field corpus read as "we looked and could not tell", which is a
|
|
27
|
+
* stronger claim than "we had not started looking". The ledger's own `parseRows`
|
|
28
|
+
* maps both to `unknown` — correct for a consumer that only needs a role — so this
|
|
29
|
+
* reads the JSON directly rather than through it, and uses `isWriterRole` for the
|
|
30
|
+
* vocabulary so the two cannot drift.
|
|
31
|
+
*
|
|
32
|
+
* PRIVACY, at the precision the code holds. Output is counts, plus the SCAN ROOT,
|
|
33
|
+
* which the caller supplied and which is the one thing a reader needs in order to
|
|
34
|
+
* know what was measured. Nothing else crosses: no session id, no ledger filename,
|
|
35
|
+
* no rule id, no timestamp. The scan reads those and keeps none of them.
|
|
36
|
+
*
|
|
37
|
+
* WRITES NOTHING. It opens files for reading and creates no directory, not even
|
|
38
|
+
* the ledger directory it looks for. A missing directory is a reported state, not
|
|
39
|
+
* a thing to fix.
|
|
40
|
+
*
|
|
41
|
+
* Exit: 0 always, except a usage error (1). Advisory — it gates nothing.
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
import * as fs from 'node:fs';
|
|
45
|
+
import * as path from 'node:path';
|
|
46
|
+
import process from 'node:process';
|
|
47
|
+
import { fileURLToPath } from 'node:url';
|
|
48
|
+
|
|
49
|
+
import { OBLIGATION_STATE_DIR, WRITER_ROLES, isWriterRole } from './_lib/obligations.js';
|
|
50
|
+
|
|
51
|
+
/** The reported classes: the closed vocabulary, plus the pre-field case. */
|
|
52
|
+
export const REPORTED_CLASSES = [...WRITER_ROLES, 'absent'] as const;
|
|
53
|
+
export type ReportedClass = (typeof REPORTED_CLASSES)[number];
|
|
54
|
+
|
|
55
|
+
/** The three row arrays a ledger carries, named as the file names them. */
|
|
56
|
+
export const ROW_ARRAYS = ['delivered', 'discharged', 'shadow'] as const;
|
|
57
|
+
export type RowArray = (typeof ROW_ARRAYS)[number];
|
|
58
|
+
|
|
59
|
+
export type Tally = Record<ReportedClass, number>;
|
|
60
|
+
|
|
61
|
+
export interface WriterSplit {
|
|
62
|
+
/** The directory scanned, echoed so a reader knows what was measured. */
|
|
63
|
+
readonly store: string;
|
|
64
|
+
/** `false` when the ledger directory does not exist at all. */
|
|
65
|
+
readonly storeExists: boolean;
|
|
66
|
+
/** Ledger files opened and parsed. */
|
|
67
|
+
readonly ledgers: number;
|
|
68
|
+
/** Files present but unreadable or unparseable — reported, never silently dropped. */
|
|
69
|
+
readonly unreadable: number;
|
|
70
|
+
/** Per row array, the split by writer class. */
|
|
71
|
+
readonly byArray: Record<RowArray, Tally>;
|
|
72
|
+
/** Every array folded together. */
|
|
73
|
+
readonly total: Tally;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function emptyTally(): Tally {
|
|
77
|
+
return { package: 0, consumer: 0, unknown: 0, absent: 0 };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Classify one row's writer field.
|
|
82
|
+
*
|
|
83
|
+
* A field that is missing entirely is `absent`. A field that is present but
|
|
84
|
+
* outside the closed vocabulary is `unknown`, matching `parseRows`: a corrupt
|
|
85
|
+
* provenance label degrades, it never deletes the record of a real row.
|
|
86
|
+
*/
|
|
87
|
+
export function classifyRow(row: unknown): ReportedClass {
|
|
88
|
+
if (typeof row !== 'object' || row === null) return 'absent';
|
|
89
|
+
if (!('writer' in row)) return 'absent';
|
|
90
|
+
const writer = (row as { writer: unknown }).writer;
|
|
91
|
+
return isWriterRole(writer) ? writer : 'unknown';
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Tally one ledger's arrays into an accumulator. */
|
|
95
|
+
function tallyLedger(doc: unknown, into: Record<RowArray, Tally>): void {
|
|
96
|
+
if (typeof doc !== 'object' || doc === null) return;
|
|
97
|
+
for (const name of ROW_ARRAYS) {
|
|
98
|
+
const rows = (doc as Record<string, unknown>)[name];
|
|
99
|
+
if (!Array.isArray(rows)) continue;
|
|
100
|
+
for (const row of rows) into[name][classifyRow(row)] += 1;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Read every ledger under `root` and split its rows by writer class.
|
|
106
|
+
*
|
|
107
|
+
* Opens files for reading only. A file that will not parse increments
|
|
108
|
+
* `unreadable` rather than being skipped in silence — a corpus with unreadable
|
|
109
|
+
* members is a different corpus from a clean one, and a reader deciding whether
|
|
110
|
+
* a sample floor is met needs to know which they have.
|
|
111
|
+
*/
|
|
112
|
+
export function readWriterSplit(root: string): WriterSplit {
|
|
113
|
+
const store = path.join(root, OBLIGATION_STATE_DIR);
|
|
114
|
+
const byArray: Record<RowArray, Tally> = {
|
|
115
|
+
delivered: emptyTally(),
|
|
116
|
+
discharged: emptyTally(),
|
|
117
|
+
shadow: emptyTally(),
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
let entries: string[];
|
|
121
|
+
try {
|
|
122
|
+
entries = fs.readdirSync(store).filter((f) => f.endsWith('.json'));
|
|
123
|
+
} catch {
|
|
124
|
+
return { store, storeExists: false, ledgers: 0, unreadable: 0, byArray, total: emptyTally() };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
let ledgers = 0;
|
|
128
|
+
let unreadable = 0;
|
|
129
|
+
for (const entry of entries) {
|
|
130
|
+
let doc: unknown;
|
|
131
|
+
try {
|
|
132
|
+
doc = JSON.parse(fs.readFileSync(path.join(store, entry), 'utf-8'));
|
|
133
|
+
} catch {
|
|
134
|
+
unreadable += 1;
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
ledgers += 1;
|
|
138
|
+
tallyLedger(doc, byArray);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const total = emptyTally();
|
|
142
|
+
for (const name of ROW_ARRAYS) {
|
|
143
|
+
for (const cls of REPORTED_CLASSES) total[cls] += byArray[name][cls];
|
|
144
|
+
}
|
|
145
|
+
return { store, storeExists: true, ledgers, unreadable, byArray, total };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const sum = (t: Tally): number => REPORTED_CLASSES.reduce((n, c) => n + t[c], 0);
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Render the split as text.
|
|
152
|
+
*
|
|
153
|
+
* The line a reader is most likely to quote is the shadow row, so it is named
|
|
154
|
+
* rather than left to be inferred from a table: the pre-registered bar counts
|
|
155
|
+
* shadow rows and nothing else, and a split over `delivered` answers a question
|
|
156
|
+
* the bar did not ask.
|
|
157
|
+
*/
|
|
158
|
+
export function render(split: WriterSplit): string {
|
|
159
|
+
const out: string[] = [];
|
|
160
|
+
out.push(`scanned: ${split.store}`);
|
|
161
|
+
if (!split.storeExists) {
|
|
162
|
+
out.push(' the ledger directory does not exist — nothing was measured.');
|
|
163
|
+
out.push(' This is not a reading of zero. It is the absence of a corpus.');
|
|
164
|
+
return `${out.join('\n')}\n`;
|
|
165
|
+
}
|
|
166
|
+
out.push(` ledgers: ${split.ledgers}${split.unreadable > 0 ? `, unreadable: ${split.unreadable}` : ''}`);
|
|
167
|
+
if (split.ledgers === 0) {
|
|
168
|
+
out.push(' no ledger files — the directory exists and holds nothing.');
|
|
169
|
+
return `${out.join('\n')}\n`;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const width = Math.max(...ROW_ARRAYS.map((n) => n.length));
|
|
173
|
+
out.push('');
|
|
174
|
+
out.push(`${'rows'.padEnd(width)} ${REPORTED_CLASSES.map((c) => c.padStart(9)).join('')} total`);
|
|
175
|
+
for (const name of ROW_ARRAYS) {
|
|
176
|
+
const t = split.byArray[name];
|
|
177
|
+
const cells = REPORTED_CLASSES.map((c) => String(t[c]).padStart(9)).join('');
|
|
178
|
+
out.push(`${name.padEnd(width)} ${cells} ${String(sum(t)).padStart(7)}`);
|
|
179
|
+
}
|
|
180
|
+
const cells = REPORTED_CLASSES.map((c) => String(split.total[c]).padStart(9)).join('');
|
|
181
|
+
out.push(`${'all'.padEnd(width)} ${cells} ${String(sum(split.total)).padStart(7)}`);
|
|
182
|
+
|
|
183
|
+
out.push('');
|
|
184
|
+
out.push(
|
|
185
|
+
`shadow rows — the only array the pre-registered bar counts: ${sum(split.byArray.shadow)}`,
|
|
186
|
+
);
|
|
187
|
+
out.push(
|
|
188
|
+
'`absent` is a row written before the writer field existed; `unknown` is a row whose',
|
|
189
|
+
);
|
|
190
|
+
out.push('writer was resolved and came back unresolvable. They are not the same reading.');
|
|
191
|
+
out.push('');
|
|
192
|
+
out.push('This is a split, not a verdict. Whether the corpus is fit to read the bar off,');
|
|
193
|
+
out.push('and what follows for the detector, are owner decisions this tool does not make.');
|
|
194
|
+
return `${out.join('\n')}\n`;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Read one flag's value, refusing a flag whose value is missing.
|
|
199
|
+
*
|
|
200
|
+
* A flag present as the last argv element must be a usage error and never a
|
|
201
|
+
* silent fall back to the default. The same slip in a sibling reporter measured
|
|
202
|
+
* the DEFAULT corpus while printing what read as a settled number — a confident
|
|
203
|
+
* figure from a tree nobody asked for is worse than no figure at all.
|
|
204
|
+
*/
|
|
205
|
+
function argValue(argv: string[], flag: string, fallback: string): string {
|
|
206
|
+
const i = argv.indexOf(flag);
|
|
207
|
+
if (i === -1) return fallback;
|
|
208
|
+
const value = argv[i + 1];
|
|
209
|
+
if (value === undefined || value.startsWith('--')) {
|
|
210
|
+
process.stderr.write(`report_obligation_writer_split: ${flag} needs a value\n`);
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|
|
213
|
+
return value;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export function main(argv: string[]): number {
|
|
217
|
+
if (argv.includes('--help') || argv.includes('-h')) {
|
|
218
|
+
process.stdout.write(
|
|
219
|
+
'usage: report_obligation_writer_split [--root <dir>] [--json]\n\n' +
|
|
220
|
+
' --root <dir> tree whose agents/runtime/state/obligations/ to read (default: cwd)\n' +
|
|
221
|
+
' --json emit the split as JSON instead of text\n\n' +
|
|
222
|
+
'Read-only. Writes nothing, gates nothing, exits 0.\n',
|
|
223
|
+
);
|
|
224
|
+
return 0;
|
|
225
|
+
}
|
|
226
|
+
const root = argValue(argv, '--root', process.cwd());
|
|
227
|
+
const split = readWriterSplit(root);
|
|
228
|
+
process.stdout.write(argv.includes('--json') ? `${JSON.stringify(split, null, 2)}\n` : render(split));
|
|
229
|
+
return 0;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const _HERE = fileURLToPath(import.meta.url);
|
|
233
|
+
|
|
234
|
+
if (process.argv[1] && path.resolve(process.argv[1]) === path.resolve(_HERE)) {
|
|
235
|
+
process.exit(main(process.argv.slice(2)));
|
|
236
|
+
}
|
|
@@ -53,8 +53,21 @@
|
|
|
53
53
|
* `command-only` and `flow-only` name a CANDIDATE for `user-invocable: false`,
|
|
54
54
|
* never a decision — see the sole-entry limit above.
|
|
55
55
|
*
|
|
56
|
+
* THE PER-PROFILE MENU, added by `road-to-a-menu-whose-precision-is-measured`
|
|
57
|
+
* 4.1. `--profile minimal|balanced|full|all` prints the menu byte count each
|
|
58
|
+
* shipped install profile delivers. Each profile's preset is read from its own
|
|
59
|
+
* file, and the answer is that all three are equal: no shipped preset declares
|
|
60
|
+
* a key that selects skills, so each one ships the whole menu.
|
|
61
|
+
*
|
|
62
|
+
* The equality note is WITHHELD when any profile carries a key listed in
|
|
63
|
+
* {@link SKILL_SELECTING_INI_KEYS}, and the key is named instead. That is the
|
|
64
|
+
* whole falsifiability claim, stated no wider than it holds: the byte count
|
|
65
|
+
* would not move — nothing here knows what such a key means — but the report
|
|
66
|
+
* stops asserting a conclusion whose premise has changed.
|
|
67
|
+
*
|
|
56
68
|
* Usage:
|
|
57
69
|
* ./scripts-run src/scripts/report_skill_menu_census # stdout summary
|
|
70
|
+
* ./scripts-run src/scripts/report_skill_menu_census --profile all
|
|
58
71
|
* ./scripts-run src/scripts/report_skill_menu_census --emit # write the artifact
|
|
59
72
|
* ./scripts-run src/scripts/report_skill_menu_census --emit --pin <sha>
|
|
60
73
|
*
|
|
@@ -313,8 +326,203 @@ function headSha(root: string): string {
|
|
|
313
326
|
return execFileSync('git', ['-C', root, 'rev-parse', 'HEAD'], { encoding: 'utf-8' }).trim();
|
|
314
327
|
}
|
|
315
328
|
|
|
329
|
+
// ── per-profile menu bytes (road-to-a-menu-whose-precision-is-measured 4.1) ──
|
|
330
|
+
|
|
331
|
+
/** The shipped install presets, mirroring `install.ts` SUPPORTED_PROFILES. */
|
|
332
|
+
export const PROFILES = ['minimal', 'balanced', 'full'] as const;
|
|
333
|
+
export type Profile = (typeof PROFILES)[number];
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Preset keys that would narrow the skill menu.
|
|
337
|
+
*
|
|
338
|
+
* EMPTY, and the emptiness is the finding rather than a stub. A preset selects
|
|
339
|
+
* `rule_loading_tier`, `discipline_profile` and `chat_history_frequency`; which
|
|
340
|
+
* SKILLS are written is decided by `packs:` frontmatter against the active pack
|
|
341
|
+
* set, which no preset names.
|
|
342
|
+
*
|
|
343
|
+
* It is a TRIPWIRE, not a selector, and the distinction is worth stating
|
|
344
|
+
* plainly because the first version of this comment overclaimed it. Nothing
|
|
345
|
+
* here knows what such a key would MEAN, so nothing here can apply one: the
|
|
346
|
+
* byte count would not move. What the list buys is that a preset carrying a
|
|
347
|
+
* listed key is NAMED in the output and the equality note is withheld — so the
|
|
348
|
+
* report stops asserting a conclusion whose premise has changed, instead of
|
|
349
|
+
* printing the same prose over a different tree.
|
|
350
|
+
*/
|
|
351
|
+
export const SKILL_SELECTING_INI_KEYS: readonly string[] = [];
|
|
352
|
+
|
|
353
|
+
/** Flat `key=value` read of a profile preset; `;` comments and blanks dropped. */
|
|
354
|
+
export function readProfileIni(root: string, profile: Profile): Record<string, string> {
|
|
355
|
+
const p = path.join(root, 'src', 'config', 'profiles', `${profile}.ini`);
|
|
356
|
+
const out: Record<string, string> = {};
|
|
357
|
+
for (const raw of fs.readFileSync(p, 'utf-8').split('\n')) {
|
|
358
|
+
const line = raw.trim();
|
|
359
|
+
if (line === '' || line.startsWith(';') || line.startsWith('#') || line.startsWith('[')) continue;
|
|
360
|
+
const eq = line.indexOf('=');
|
|
361
|
+
if (eq === -1) continue;
|
|
362
|
+
out[line.slice(0, eq).trim()] = line.slice(eq + 1).trim();
|
|
363
|
+
}
|
|
364
|
+
return out;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
export interface ProfileMenu {
|
|
368
|
+
readonly profile: Profile;
|
|
369
|
+
/** Skills this profile writes onto the model menu. */
|
|
370
|
+
readonly menuSkills: number;
|
|
371
|
+
/** Bytes of `- <name>: <description>\n`, the shape `censusSkillsCatalog` sums. */
|
|
372
|
+
readonly menuBytes: number;
|
|
373
|
+
/** chars/4, the estimator the payload budget uses. */
|
|
374
|
+
readonly tokensEstimate: number;
|
|
375
|
+
/** `declared_skill_catalogue_tokens` from the preset, or null when absent. */
|
|
376
|
+
readonly declaredCatalogueTokens: number | null;
|
|
377
|
+
/** Preset keys found that would narrow the menu. Empty today, by measurement. */
|
|
378
|
+
readonly selectingKeys: readonly string[];
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* One profile's menu, computed from its preset plus the skill tree.
|
|
383
|
+
*
|
|
384
|
+
* The catalogue LINE is the same shape `preamble_byte_census.censusSkillsCatalog`
|
|
385
|
+
* sums — `- <name>: <description>\n`. The POPULATION deliberately is not, and the
|
|
386
|
+
* difference is stated rather than smoothed over: that census sums every
|
|
387
|
+
* `SKILL.md`, because the payload bucket it feeds is about what the host lists;
|
|
388
|
+
* this one drops a skill carrying `user-invocable: false` or
|
|
389
|
+
* `disable-model-invocation: true`, because a menu is what the MODEL may pick
|
|
390
|
+
* from. Two skills carry such a flag today, so over the same tree and the same
|
|
391
|
+
* line shape this report reads 311 B below the full-population sum. Expect the
|
|
392
|
+
* two to differ; a reader who finds them equal has found a defect in one.
|
|
393
|
+
*/
|
|
394
|
+
export function menuForProfile(root: string, profile: Profile): ProfileMenu {
|
|
395
|
+
const ini = readProfileIni(root, profile);
|
|
396
|
+
const selectingKeys = SKILL_SELECTING_INI_KEYS.filter((k) => k in ini);
|
|
397
|
+
const skillsDir = path.join(root, 'src', 'skills');
|
|
398
|
+
let menuSkills = 0;
|
|
399
|
+
let menuBytes = 0;
|
|
400
|
+
for (const entry of fs.readdirSync(skillsDir, { withFileTypes: true })) {
|
|
401
|
+
if (!entry.isDirectory()) continue;
|
|
402
|
+
const skillMd = path.join(skillsDir, entry.name, 'SKILL.md');
|
|
403
|
+
if (!fs.existsSync(skillMd)) continue;
|
|
404
|
+
const text = fs.readFileSync(skillMd, 'utf-8');
|
|
405
|
+
const flags = menuFlags(text);
|
|
406
|
+
if (flags.userInvocable === false || flags.disableModel === true) continue;
|
|
407
|
+
const name = /^name:\s*(\S.*?)\s*$/mu.exec(text.slice(0, 4000))?.[1] ?? entry.name;
|
|
408
|
+
const description = frontmatterDescription(text);
|
|
409
|
+
menuSkills += 1;
|
|
410
|
+
menuBytes += `- ${name}: ${description}\n`.length;
|
|
411
|
+
}
|
|
412
|
+
const declared = ini['declared_skill_catalogue_tokens'];
|
|
413
|
+
return {
|
|
414
|
+
profile,
|
|
415
|
+
menuSkills,
|
|
416
|
+
menuBytes,
|
|
417
|
+
tokensEstimate: Math.round(menuBytes / 4),
|
|
418
|
+
declaredCatalogueTokens: declared === undefined ? null : Number(declared),
|
|
419
|
+
selectingKeys,
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* The `description:` value, folded lines included.
|
|
425
|
+
*
|
|
426
|
+
* A `[^\n]+` capture was measured 17.1 % low elsewhere in this tree because it
|
|
427
|
+
* truncates every folded multi-line description; this reads to the next
|
|
428
|
+
* top-level key instead.
|
|
429
|
+
*
|
|
430
|
+
* A double-quoted value has its escapes DECODED. Leaving them encoded counted
|
|
431
|
+
* `\"` as two characters and over-reported the menu by 56 B across 13 skills —
|
|
432
|
+
* small, but the same class of defect as the 17.1 % one above, and a function
|
|
433
|
+
* whose docstring cites that fix should not carry a second instance of it.
|
|
434
|
+
*/
|
|
435
|
+
export function frontmatterDescription(text: string): string {
|
|
436
|
+
if (!text.startsWith('---')) return '';
|
|
437
|
+
const end = text.indexOf('\n---', 3);
|
|
438
|
+
if (end === -1) return '';
|
|
439
|
+
const lines = text.slice(3, end).split('\n');
|
|
440
|
+
const at = lines.findIndex((l) => /^description:/u.test(l));
|
|
441
|
+
if (at === -1) return '';
|
|
442
|
+
const parts = [(lines[at] as string).replace(/^description:\s*/u, '')];
|
|
443
|
+
for (let i = at + 1; i < lines.length; i += 1) {
|
|
444
|
+
const l = lines[i] as string;
|
|
445
|
+
if (/^\S/u.test(l)) break;
|
|
446
|
+
parts.push(l.trim());
|
|
447
|
+
}
|
|
448
|
+
const joined = parts.join(' ').trim();
|
|
449
|
+
if (joined.length >= 2 && joined.startsWith('"') && joined.endsWith('"')) {
|
|
450
|
+
return joined.slice(1, -1).replace(/\\(["\\])/gu, '$1');
|
|
451
|
+
}
|
|
452
|
+
if (joined.length >= 2 && joined.startsWith("'") && joined.endsWith("'")) {
|
|
453
|
+
return joined.slice(1, -1).replace(/''/gu, "'");
|
|
454
|
+
}
|
|
455
|
+
return joined;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/** Render the per-profile menu table. Returns the lines, so tests read them. */
|
|
459
|
+
export function renderProfileMenus(menus: readonly ProfileMenu[]): string[] {
|
|
460
|
+
const L: string[] = [];
|
|
461
|
+
L.push('menu bytes per install profile (name + description, the catalogue shape):');
|
|
462
|
+
L.push(' profile skills menu_bytes ~tokens declared');
|
|
463
|
+
for (const m of menus) {
|
|
464
|
+
L.push(
|
|
465
|
+
` ${m.profile.padEnd(10)} ${String(m.menuSkills).padStart(6)} ` +
|
|
466
|
+
`${String(m.menuBytes).padStart(12)} ${String(m.tokensEstimate).padStart(9)} ` +
|
|
467
|
+
`${(m.declaredCatalogueTokens === null ? '—' : String(m.declaredCatalogueTokens)).padStart(10)}`,
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
const flagged = menus.filter((m) => m.selectingKeys.length > 0);
|
|
471
|
+
if (flagged.length > 0) {
|
|
472
|
+
L.push('');
|
|
473
|
+
L.push(' A preset declares a key that could select skills, so the equality below is NOT');
|
|
474
|
+
L.push(' asserted — the premise it rested on has changed. Keys found:');
|
|
475
|
+
for (const m of flagged) L.push(` ${m.profile}: ${m.selectingKeys.join(', ')}`);
|
|
476
|
+
return L;
|
|
477
|
+
}
|
|
478
|
+
const distinct = new Set(menus.map((m) => m.menuBytes));
|
|
479
|
+
if (menus.length > 1 && distinct.size === 1) {
|
|
480
|
+
L.push('');
|
|
481
|
+
L.push(
|
|
482
|
+
' All profiles report the SAME menu_bytes, and that is a measurement, not a',
|
|
483
|
+
);
|
|
484
|
+
L.push(
|
|
485
|
+
' placeholder: no preset declares a key that selects skills, so each ships the',
|
|
486
|
+
);
|
|
487
|
+
L.push(
|
|
488
|
+
' whole menu. The preset chooses which RULES activate; which SKILLS are written',
|
|
489
|
+
);
|
|
490
|
+
L.push(
|
|
491
|
+
' is decided by `packs:` frontmatter against the active pack set. A menu-bytes',
|
|
492
|
+
);
|
|
493
|
+
L.push(
|
|
494
|
+
' lever therefore has to move packs or the menu flags — changing profile does',
|
|
495
|
+
);
|
|
496
|
+
L.push(' nothing. The three presets already record this in their own comments.');
|
|
497
|
+
}
|
|
498
|
+
return L;
|
|
499
|
+
}
|
|
500
|
+
|
|
316
501
|
export function main(argv: readonly string[]): number {
|
|
317
502
|
const root = REPO_ROOT;
|
|
503
|
+
const profIdx = argv.indexOf('--profile');
|
|
504
|
+
if (profIdx !== -1) {
|
|
505
|
+
const want = argv[profIdx + 1] ?? 'all';
|
|
506
|
+
const chosen: Profile[] =
|
|
507
|
+
want === 'all' ? [...PROFILES] : PROFILES.includes(want as Profile) ? [want as Profile] : [];
|
|
508
|
+
if (chosen.length === 0) {
|
|
509
|
+
process.stderr.write(
|
|
510
|
+
`report_skill_menu_census: --profile expects ${PROFILES.join(' | ')} | all, got ${want}\n`,
|
|
511
|
+
);
|
|
512
|
+
return 2;
|
|
513
|
+
}
|
|
514
|
+
if (argv.includes('--emit')) {
|
|
515
|
+
// Refused rather than ignored: `--emit` writes the census artifact,
|
|
516
|
+
// `--profile` prints a table and writes nothing, and silently doing
|
|
517
|
+
// the second when the caller asked for both is how an artifact goes
|
|
518
|
+
// un-refreshed without anyone noticing.
|
|
519
|
+
process.stderr.write('report_skill_menu_census: --profile and --emit are separate runs\n');
|
|
520
|
+
return 2;
|
|
521
|
+
}
|
|
522
|
+
const menus = chosen.map((p) => menuForProfile(root, p));
|
|
523
|
+
process.stdout.write(`${renderProfileMenus(menus).join('\n')}\n`);
|
|
524
|
+
return 0;
|
|
525
|
+
}
|
|
318
526
|
const emit = argv.includes('--emit');
|
|
319
527
|
const pinIdx = argv.indexOf('--pin');
|
|
320
528
|
const pin = pinIdx !== -1 && pinIdx + 1 < argv.length ? (argv[pinIdx + 1] as string) : headSha(root);
|
|
@@ -49,13 +49,20 @@ import * as path from 'node:path';
|
|
|
49
49
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
50
50
|
|
|
51
51
|
import {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
} from './_lib/rule_injection.js';
|
|
52
|
+
corpusShapeProblems,
|
|
53
|
+
slotFireSizes as sampleSlotFireSizes,
|
|
54
|
+
type SlotFireSizes,
|
|
55
|
+
} from './_lib/activation_payload.js';
|
|
56
|
+
import { loadRouter, pathOnlyRuleIds } from './_lib/rule_injection.js';
|
|
57
57
|
import { CAP_BYTES } from './hooks/rule_inject_hook.js';
|
|
58
58
|
|
|
59
|
+
// Re-exported rather than redefined: this module was their only home until the
|
|
60
|
+
// payload gate needed the same sample, and two copies is how two numbers start
|
|
61
|
+
// disagreeing. `corpusShapeProblems` and `SlotFireSizes` are imported above
|
|
62
|
+
// because this module's own code uses them; these two it only passes through.
|
|
63
|
+
export { percentile, readCorpusPositives } from './_lib/activation_payload.js';
|
|
64
|
+
export { corpusShapeProblems, type SlotFireSizes };
|
|
65
|
+
|
|
59
66
|
const _HERE = fileURLToPath(import.meta.url);
|
|
60
67
|
export const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
|
|
61
68
|
export const ARTIFACT_REL = path.join(
|
|
@@ -149,8 +156,13 @@ export const HOST_SURFACES: readonly HostSurface[] = [
|
|
|
149
156
|
},
|
|
150
157
|
{
|
|
151
158
|
host: 'codex', surface: '.codex/agent-config.md', perRuleTree: false,
|
|
152
|
-
writer: 'src/scripts/install.ts:
|
|
153
|
-
|
|
159
|
+
writer: 'src/scripts/install.ts:1431',
|
|
160
|
+
// Scoped to `.codex` rather than the bare filename: nine other lines in
|
|
161
|
+
// `install.ts` write an `agent-config.md` for some other host, so the
|
|
162
|
+
// unscoped anchor would have accepted a drift onto any of them — the
|
|
163
|
+
// bounds-test-wearing-a-provenance-test's-name failure this field exists
|
|
164
|
+
// to close, one level down.
|
|
165
|
+
anchor: '\'.codex\', \'agent-config.md\'',
|
|
154
166
|
note: 'written by the installer, not by `condense`; absent in this checkout',
|
|
155
167
|
},
|
|
156
168
|
];
|
|
@@ -431,155 +443,17 @@ export function renderArtifact(root: string, pin: string): string {
|
|
|
431
443
|
return L.join('\n');
|
|
432
444
|
}
|
|
433
445
|
|
|
434
|
-
/** One positive prompt of the frozen corpus, with the open files it declares. */
|
|
435
|
-
interface CorpusPrompt {
|
|
436
|
-
prompt: string;
|
|
437
|
-
openFiles: string[] | null;
|
|
438
|
-
}
|
|
439
|
-
|
|
440
446
|
/**
|
|
441
|
-
*
|
|
447
|
+
* The activation-charge sample at the concern's own cap.
|
|
442
448
|
*
|
|
443
|
-
*
|
|
444
|
-
*
|
|
445
|
-
*
|
|
446
|
-
*
|
|
447
|
-
*
|
|
448
|
-
* measurement that governs a budget must fail loudly on a shape it cannot read
|
|
449
|
-
* rather than under-count, so the CLI refuses on a non-empty result here.
|
|
450
|
-
*
|
|
451
|
-
* Reported as a shape problem rather than fixed by widening the parser: this
|
|
452
|
-
* script must not grow a second YAML implementation, and the corpus is frozen,
|
|
453
|
-
* so the honest answer to an unreadable shape is to say which file carries it.
|
|
454
|
-
*/
|
|
455
|
-
export function corpusShapeProblems(root: string): string[] {
|
|
456
|
-
const dir = path.join(root, 'tests', 'eval', 'routing-matrix');
|
|
457
|
-
const problems: string[] = [];
|
|
458
|
-
if (!fs.existsSync(dir)) return problems;
|
|
459
|
-
for (const f of fs.readdirSync(dir).filter((n) => n.endsWith('.yaml')).sort()) {
|
|
460
|
-
let inPositives = false;
|
|
461
|
-
let lineNo = 0;
|
|
462
|
-
for (const raw of fs.readFileSync(path.join(dir, f), 'utf-8').split('\n')) {
|
|
463
|
-
lineNo += 1;
|
|
464
|
-
if (/^positives:/u.test(raw)) { inPositives = true; continue; }
|
|
465
|
-
if (/^near_misses:/u.test(raw)) { inPositives = false; continue; }
|
|
466
|
-
if (!inPositives) continue;
|
|
467
|
-
if (/^\s*-\s*prompt:/u.test(raw) && !/^\s*-\s*prompt:\s*"(.*)"\s*$/u.test(raw)) {
|
|
468
|
-
problems.push(
|
|
469
|
-
`${f}:${String(lineNo)}: a \`- prompt:\` this parser cannot read — only a ` +
|
|
470
|
-
`double-quoted same-line scalar is accepted: ${JSON.stringify(raw.trim())}`,
|
|
471
|
-
);
|
|
472
|
-
continue;
|
|
473
|
-
}
|
|
474
|
-
if (/^\s*-\s*/u.test(raw) && !/^\s*-\s*(prompt|command|open_files):/u.test(raw)
|
|
475
|
-
&& raw.trim() !== '-' && raw.trim() !== '') {
|
|
476
|
-
// A bare sequence item under `positives:` is a shape with no
|
|
477
|
-
// `prompt:` key on its first line — folded or nested.
|
|
478
|
-
if (!/^\s{4,}/u.test(raw)) {
|
|
479
|
-
problems.push(
|
|
480
|
-
`${f}:${String(lineNo)}: a positives entry whose first line carries no ` +
|
|
481
|
-
`\`prompt:\` key: ${JSON.stringify(raw.trim())}`,
|
|
482
|
-
);
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
if (/^\s*open_files:\s*$/u.test(raw)) {
|
|
486
|
-
problems.push(
|
|
487
|
-
`${f}:${String(lineNo)}: a BLOCK-sequence \`open_files:\` — only the inline ` +
|
|
488
|
-
'`[...]` form is read, so this entry\'s files would be dropped',
|
|
489
|
-
);
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
return problems;
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
/** Every positive prompt in the frozen routing-matrix corpus. Near-misses are not fires. */
|
|
497
|
-
export function readCorpusPositives(root: string): CorpusPrompt[] {
|
|
498
|
-
const dir = path.join(root, 'tests', 'eval', 'routing-matrix');
|
|
499
|
-
const out: CorpusPrompt[] = [];
|
|
500
|
-
if (!fs.existsSync(dir)) return out;
|
|
501
|
-
for (const f of fs.readdirSync(dir).filter((n) => n.endsWith('.yaml')).sort()) {
|
|
502
|
-
let inPositives = false;
|
|
503
|
-
let cur: CorpusPrompt | null = null;
|
|
504
|
-
for (const raw of fs.readFileSync(path.join(dir, f), 'utf-8').split('\n')) {
|
|
505
|
-
if (/^positives:/u.test(raw)) { inPositives = true; continue; }
|
|
506
|
-
if (/^near_misses:/u.test(raw)) { inPositives = false; continue; }
|
|
507
|
-
if (!inPositives) continue;
|
|
508
|
-
const p = /^\s*-\s*prompt:\s*"(.*)"\s*$/u.exec(raw);
|
|
509
|
-
if (p !== null) {
|
|
510
|
-
cur = { prompt: p[1] as string, openFiles: null };
|
|
511
|
-
out.push(cur);
|
|
512
|
-
continue;
|
|
513
|
-
}
|
|
514
|
-
const of = /^\s*open_files:\s*\[(.*)\]\s*$/u.exec(raw);
|
|
515
|
-
if (of !== null && cur !== null) {
|
|
516
|
-
cur.openFiles = (of[1] as string)
|
|
517
|
-
.split(',')
|
|
518
|
-
.map((x) => x.trim().replace(/^["']|["']$/gu, ''))
|
|
519
|
-
.filter((x) => x !== '');
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
return out;
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
/** Nearest-rank percentile over an unsorted sample. Empty sample → 0. */
|
|
527
|
-
export function percentile(xs: readonly number[], q: number): number {
|
|
528
|
-
if (xs.length === 0) return 0;
|
|
529
|
-
const s = [...xs].sort((a, b) => a - b);
|
|
530
|
-
return s[Math.min(s.length - 1, Math.floor(q * (s.length - 1)))] as number;
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
export interface SlotFireSizes {
|
|
534
|
-
readonly slot: string;
|
|
535
|
-
readonly fires: number;
|
|
536
|
-
readonly p50: number;
|
|
537
|
-
readonly p90: number;
|
|
538
|
-
readonly max: number;
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
/**
|
|
542
|
-
* Gate-open per-fire emission sizes for the delivery concern, per slot
|
|
543
|
-
* (road-to-delivery-for-every-host 3.1).
|
|
544
|
-
*
|
|
545
|
-
* Measured through the SAME selection the runtime concern uses —
|
|
546
|
-
* `matchTierRules` + `selectForInjection` at the concern's own `CAP_BYTES` —
|
|
547
|
-
* rather than through a second model of it. An offline figure computed by a
|
|
548
|
-
* different matcher would price a set the concern does not deliver.
|
|
549
|
-
*
|
|
550
|
-
* `pre_compact` is 0 by construction rather than by measurement: that branch of
|
|
551
|
-
* `rule_inject_hook` clears the seen-set and returns allow without writing to
|
|
552
|
-
* stdout, so there is no emission to sample.
|
|
449
|
+
* The sampling itself moved to `_lib/activation_payload.ts` when the payload
|
|
450
|
+
* gate needed the same figure (`road-to-the-delivery-flip-that-tells-the-truth`
|
|
451
|
+
* 3.1). This wrapper supplies `CAP_BYTES` so the report's own callers keep the
|
|
452
|
+
* one-argument form, and the helpers below are re-exported so there is still
|
|
453
|
+
* exactly one implementation of each.
|
|
553
454
|
*/
|
|
554
455
|
export function slotFireSizes(root: string): SlotFireSizes[] {
|
|
555
|
-
|
|
556
|
-
const prompts = readCorpusPositives(root);
|
|
557
|
-
const sample = (mode: 'prompt' | 'file'): number[] => {
|
|
558
|
-
const out: number[] = [];
|
|
559
|
-
for (const r of prompts) {
|
|
560
|
-
if (mode === 'file' && (r.openFiles === null || r.openFiles.length === 0)) continue;
|
|
561
|
-
const matches =
|
|
562
|
-
mode === 'prompt'
|
|
563
|
-
? matchTierRules(router, r.prompt, null, null)
|
|
564
|
-
: matchTierRules(router, '', r.openFiles, null);
|
|
565
|
-
if (matches.length === 0) continue;
|
|
566
|
-
const sel = selectForInjection(root, matches, CAP_BYTES);
|
|
567
|
-
if (sel.selected.length > 0) out.push(sel.bytes);
|
|
568
|
-
}
|
|
569
|
-
return out;
|
|
570
|
-
};
|
|
571
|
-
const row = (slot: string, xs: number[]): SlotFireSizes => ({
|
|
572
|
-
slot,
|
|
573
|
-
fires: xs.length,
|
|
574
|
-
p50: percentile(xs, 0.5),
|
|
575
|
-
p90: percentile(xs, 0.9),
|
|
576
|
-
max: xs.length === 0 ? 0 : Math.max(...xs),
|
|
577
|
-
});
|
|
578
|
-
return [
|
|
579
|
-
row('user_prompt_submit', sample('prompt')),
|
|
580
|
-
row('pre_tool_use', sample('file')),
|
|
581
|
-
row('pre_compact', []),
|
|
582
|
-
];
|
|
456
|
+
return sampleSlotFireSizes(root, CAP_BYTES);
|
|
583
457
|
}
|
|
584
458
|
|
|
585
459
|
function headSha(root: string): string {
|