@event4u/agent-config 14.12.0 → 14.14.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 +118 -155
- package/CONTRIBUTING.md +38 -0
- package/CREDITS.md +135 -0
- package/NOTICE +13 -0
- package/README.md +8 -6
- package/dist/agent-src/commands/analyze/inbox.md +145 -2
- package/dist/agent-src/commands/feature/dev.md +7 -2
- package/dist/agent-src/commands/ghostwriter/write.md +5 -1
- package/dist/agent-src/commands/implement-ticket.md +1 -1
- package/dist/agent-src/commands/mission/upgrade.md +1 -1
- package/dist/agent-src/commands/orchestrate.md +2 -2
- package/dist/agent-src/commands/post-as/me.md +5 -1
- package/dist/agent-src/commands/pr/merge.md +19 -6
- package/dist/agent-src/commands/roadmap/process-full.md +19 -5
- package/dist/agent-src/commands/tdd/red.md +12 -2
- package/dist/agent-src/commands/work.md +1 -1
- package/dist/agent-src/contexts/communication/rules-auto/token-efficiency-mechanics.md +40 -0
- package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +5 -2
- package/dist/agent-src/contexts/execution/autonomy-mechanics.md +21 -0
- package/dist/agent-src/contexts/execution/orchestration-telemetry.md +1 -0
- package/dist/agent-src/contexts/execution/subagent-response-contract.md +40 -0
- package/dist/agent-src/contexts/execution/subagent-steering.md +13 -1
- package/dist/agent-src/contexts/execution/terminal-states.md +96 -0
- package/dist/agent-src/rules/artifact-drafting-protocol.md +1 -1
- package/dist/agent-src/rules/broken-access-control.md +4 -0
- package/dist/agent-src/rules/code-comment-discipline.md +6 -0
- package/dist/agent-src/rules/code-provenance.md +4 -0
- package/dist/agent-src/rules/design-fidelity.md +1 -1
- package/dist/agent-src/rules/domain-safety-pii.md +4 -0
- package/dist/agent-src/rules/domain-safety-retention.md +4 -0
- package/dist/agent-src/rules/engineering-safety-floor.md +2 -0
- package/dist/agent-src/rules/external-code-graph-interop.md +14 -12
- package/dist/agent-src/rules/improve-before-implement.md +6 -8
- package/dist/agent-src/rules/laravel-routing.md +1 -1
- package/dist/agent-src/rules/lethal-trifecta-guard.md +4 -0
- package/dist/agent-src/rules/secret-vcs-guard.md +2 -0
- package/dist/agent-src/rules/security-sensitive-stop.md +2 -0
- package/dist/agent-src/rules/source-confidentiality.md +12 -34
- package/dist/agent-src/rules/symfony-routing.md +1 -1
- package/dist/agent-src/rules/token-efficiency.md +14 -0
- package/dist/agent-src/rules/tool-safety.md +4 -0
- package/dist/agent-src/rules/untrusted-input-defense.md +4 -0
- package/dist/agent-src/scripts/archive_completed_roadmaps.ts +252 -8
- package/dist/agent-src/scripts/guarded_baseline.ts +343 -0
- package/dist/agent-src/scripts/roadmap_set_step.ts +253 -0
- package/dist/agent-src/scripts/stub_queue.ts +82 -0
- package/dist/agent-src/scripts/stubs_due.ts +516 -0
- package/dist/agent-src/scripts/ticket_bundles.ts +84 -0
- package/dist/agent-src/scripts/update_roadmap_progress.ts +16 -12
- package/dist/agent-src/skills/brand-asset-generation/SKILL.md +17 -4
- package/dist/agent-src/skills/brand-audit/SKILL.md +33 -0
- package/dist/agent-src/skills/code-intelligence/SKILL.md +57 -0
- package/dist/agent-src/skills/database/SKILL.md +8 -0
- package/dist/agent-src/skills/dependency-upgrade/SKILL.md +27 -0
- package/dist/agent-src/skills/design-review/SKILL.md +1 -1
- package/dist/agent-src/skills/doc-coauthoring/SKILL.md +1 -1
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +17 -1
- package/dist/agent-src/skills/feature-planning/SKILL.md +9 -2
- package/dist/agent-src/skills/frontend-render-security/SKILL.md +44 -0
- package/dist/agent-src/skills/git-workflow/SKILL.md +1 -1
- package/dist/agent-src/skills/js-library-packaging/scripts/check_package_surface.ts +38 -4
- package/dist/agent-src/skills/judge-artifact-completeness/rubrics/ticket-quality-score.json +2 -2
- package/dist/agent-src/skills/laravel/SKILL.md +35 -0
- package/dist/agent-src/skills/laravel-migration/SKILL.md +57 -3
- package/dist/agent-src/skills/monorepo-workspace/SKILL.md +26 -0
- package/dist/agent-src/skills/playbook-authoring/SKILL.md +28 -1
- package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +1 -1
- package/dist/agent-src/skills/skill-writing/references/procedure.md +5 -5
- package/dist/agent-src/skills/sql-writing/SKILL.md +7 -1
- package/dist/agent-src/skills/standards-from-config/SKILL.md +27 -4
- package/dist/agent-src/skills/supply-chain-intake/SKILL.md +11 -4
- package/dist/agent-src/skills/symfony-workflow/SKILL.md +35 -0
- package/dist/agent-src/skills/test-case-discovery/SKILL.md +85 -0
- package/dist/agent-src/skills/test-driven-development/SKILL.md +33 -9
- package/dist/agent-src/skills/testing-anti-patterns/SKILL.md +1 -1
- package/dist/agent-src/skills/testing-anti-patterns/process-anti-patterns.md +37 -0
- package/dist/agent-src/skills/ui-component-architect/SKILL.md +45 -2
- package/dist/agent-src/skills/upstream-contribute/SKILL.md +38 -1
- package/dist/agent-src/skills/verify-repair-loop/SKILL.md +9 -4
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/roadmaps.md +97 -3
- package/dist/agent-src/templates/scripts/check_memory.ts +258 -17
- package/dist/agent-src/templates/scripts/check_memory_proposal.ts +9 -3
- package/dist/agent-src/templates/scripts/memory_lookup.ts +86 -0
- package/dist/agent-src/templates/scripts/memory_signal.ts +44 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/implement.ts +90 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/index.ts +11 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/test.ts +23 -1
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +10 -1
- package/dist/agent-src/templates/scripts/work_engine/directives/mixed/stitch.ts +5 -1
- package/dist/agent-src/templates/scripts/work_engine/stack/runner.ts +102 -14
- package/dist/cli/commands/uiAudit.js +124 -5
- package/dist/cli/registry.js +2 -0
- package/dist/cli-delegate/{chunk-HYWSRNUX.js → chunk-2DK2VJWG.js} +3 -3
- package/dist/cli-delegate/{chunk-JCW7ZLHK.js → chunk-4FS4WRXP.js} +5 -0
- package/dist/cli-delegate/{chunk-OMIVVCG2.js → chunk-A4QJ6ENT.js} +24 -0
- package/dist/cli-delegate/{chunk-JJA2QEWA.js → chunk-AKJGUQNO.js} +466 -285
- package/dist/cli-delegate/{chunk-E46KPN6K.js → chunk-BTWUYDGN.js} +424 -363
- package/dist/cli-delegate/{chunk-K4FFODTS.js → chunk-ILMUPLGW.js} +1227 -316
- package/dist/cli-delegate/{chunk-FF7WAFMR.js → chunk-KPBWFOPZ.js} +5 -1
- package/dist/cli-delegate/cmd_adr_effective.js +102 -0
- package/dist/cli-delegate/cmd_conformance.js +80 -11
- package/dist/cli-delegate/cmd_doctor.js +3 -3
- package/dist/cli-delegate/cmd_export.js +3 -2
- package/dist/cli-delegate/cmd_fleet.js +2 -2
- package/dist/cli-delegate/cmd_handoff.js +1 -1
- package/dist/cli-delegate/cmd_preflight.js +2 -2
- package/dist/cli-delegate/cmd_prune.js +3 -2
- package/dist/cli-delegate/cmd_settings_get.js +1 -1
- package/dist/cli-delegate/cmd_settings_migrate.js +3 -2
- package/dist/cli-delegate/cmd_settings_set.js +2 -2
- package/dist/cli-delegate/cmd_sync.js +3 -2
- package/dist/cli-delegate/cmd_uninstall.js +3 -2
- package/dist/cli-delegate/cmd_validate.js +3 -2
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +58 -58
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +1 -1
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +2 -2
- package/dist/discovery/trust-report.md +1 -1
- package/dist/discovery/workspaces.json +2 -2
- package/dist/hooks/dispatch.js +2900 -1049
- package/dist/install/claudeRuleRewrite.js +254 -0
- package/dist/install/detect.js +29 -16
- package/dist/install/detect_php_shape.js +159 -0
- package/dist/install/install.mjs +634 -428
- package/dist/mcp/awesome-mcp-servers.row.md +1 -1
- package/dist/mcp/mcp-cloudflare-catalogue.json +1 -1
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +3 -3
- package/dist/mcp/server.mjs +4895 -2627
- package/dist/server/schemas/settings.js +3 -0
- package/dist/ui/assets/index-D0jaCRPM.js +29 -0
- package/dist/ui/index.html +1 -1
- package/docs/MIGRATION.md +1 -1
- package/docs/THIRD-PARTY-NOTICES.md +25 -0
- package/docs/contracts/provider-lifecycle.md +1 -1
- package/docs/contracts/settings-classes.md +10 -4
- package/docs/guidelines/agent-infra/5w2h-analysis.md +3 -3
- package/docs/guidelines/agent-infra/agent-interaction-and-decision-quality.md +1 -0
- package/docs/guidelines/agent-infra/artifact-drafting-protocol-mechanics.md +58 -3
- package/docs/guidelines/agent-infra/ask-when-uncertain-demos.md +2 -2
- package/docs/guidelines/agent-infra/asking-and-brevity-examples.md +3 -3
- package/docs/guidelines/agent-infra/break-glass-usage.md +7 -7
- package/docs/guidelines/agent-infra/carve-out-predicates.md +3 -3
- package/docs/guidelines/agent-infra/comparison-matrix.md +1 -1
- package/docs/guidelines/agent-infra/critical-thinking.md +4 -4
- package/docs/guidelines/agent-infra/direct-answers-demos.md +2 -2
- package/docs/guidelines/agent-infra/domain-eval-anti-pattern.md +1 -1
- package/docs/guidelines/agent-infra/engineering-memory-data-format.md +3 -3
- package/docs/guidelines/agent-infra/existence-question-verdicts.md +80 -0
- package/docs/guidelines/agent-infra/first-principles.md +2 -2
- package/docs/guidelines/agent-infra/framework-neutrality-patterns.md +2 -2
- package/docs/guidelines/agent-infra/guarded-baseline.md +39 -0
- package/docs/guidelines/agent-infra/inversion-thinking.md +5 -5
- package/docs/guidelines/agent-infra/language-and-tone-examples.md +1 -1
- package/docs/guidelines/agent-infra/layered-settings.md +10 -10
- package/docs/guidelines/agent-infra/memory-access.md +1 -1
- package/docs/guidelines/agent-infra/mental-models.md +3 -3
- package/docs/guidelines/agent-infra/reuse-verdict-mechanics.md +71 -0
- package/docs/guidelines/agent-infra/review-routing-data-format.md +7 -7
- package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +13 -7
- package/docs/guidelines/agent-infra/role-contracts.md +2 -2
- package/docs/guidelines/agent-infra/role-mode-router.md +3 -3
- package/docs/guidelines/agent-infra/rule-type-governance.md +2 -2
- package/docs/guidelines/agent-infra/scqa-framework.md +5 -5
- package/docs/guidelines/agent-infra/self-improvement-pipeline.md +6 -6
- package/docs/guidelines/agent-infra/simplicity-and-goal-demos.md +2 -2
- package/docs/guidelines/agent-infra/six-hats.md +3 -3
- package/docs/guidelines/agent-infra/skill-quality-checklist.md +6 -6
- package/docs/guidelines/agent-infra/source-confidentiality-mechanics.md +109 -0
- package/docs/guidelines/agent-infra/systems-thinking.md +1 -1
- package/docs/guidelines/agent-infra/verify-before-complete-demos.md +2 -2
- package/docs/guidelines/augment-portability-patterns.md +6 -4
- package/docs/guidelines/code-clarity.md +18 -0
- package/docs/guidelines/component-oriented-and-oop-development.md +3 -2
- package/docs/guidelines/cross-role-handoff.md +2 -2
- package/docs/guidelines/design-fidelity-mechanics.md +1 -1
- package/docs/guidelines/docs/readme-size-and-splitting.md +3 -3
- package/docs/guidelines/php/database.md +90 -7
- package/docs/guidelines/prompt-templates.md +7 -7
- package/llms.txt +2 -2
- package/package.json +5 -2
- package/src/agent-src/scripts/archive_completed_roadmaps.ts +252 -8
- package/src/agent-src/scripts/guarded_baseline.ts +343 -0
- package/src/agent-src/scripts/roadmap_set_step.ts +253 -0
- package/src/agent-src/scripts/stub_queue.ts +82 -0
- package/src/agent-src/scripts/stubs_due.ts +516 -0
- package/src/agent-src/scripts/ticket_bundles.ts +84 -0
- package/src/agent-src/scripts/update_roadmap_progress.ts +16 -12
- package/src/agent-src/templates/scripts/check_memory.ts +258 -17
- package/src/agent-src/templates/scripts/check_memory_proposal.ts +9 -3
- package/src/agent-src/templates/scripts/memory_lookup.ts +86 -0
- package/src/agent-src/templates/scripts/memory_signal.ts +44 -0
- package/src/agent-src/templates/scripts/work_engine/directives/backend/implement.ts +90 -0
- package/src/agent-src/templates/scripts/work_engine/directives/backend/index.ts +11 -0
- package/src/agent-src/templates/scripts/work_engine/directives/backend/test.ts +23 -1
- package/src/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +10 -1
- package/src/agent-src/templates/scripts/work_engine/directives/mixed/stitch.ts +5 -1
- package/src/agent-src/templates/scripts/work_engine/stack/runner.ts +102 -14
- package/src/config/agent-settings.template.yml +32 -3
- package/src/config/assurance-capability-registry.json +21 -9
- package/src/config/canonical-terms.yml +119 -0
- package/src/config/capability-native-outcome-bars.json +81 -0
- package/src/config/ci-local-parity.yml +80 -0
- package/src/config/drafting-channels.yml +43 -0
- package/src/config/estate-count-budget.json +57 -36
- package/src/config/evaluator-budgets.json +14 -14
- package/src/config/gate-coverage.yml +729 -63
- package/src/config/gate-reachability-exemptions.json +21 -0
- package/src/config/gate-violation-baselines.json +42 -19
- package/src/config/harness-evolution-budget.json +25 -0
- package/src/config/lapsed-beta-baseline.json +96 -0
- package/src/config/memory-twin-verdicts.yml +129 -0
- package/src/config/metric-registry.yml +124 -0
- package/src/config/pack-size-budget.json +16 -15
- package/src/config/preamble-payload-budget.json +12 -4
- package/src/config/profiles/balanced.ini +27 -0
- package/src/config/profiles/full.ini +27 -0
- package/src/config/profiles/minimal.ini +27 -0
- package/src/config/publish-surface.json +9 -2
- package/src/config/routing-coverage-seed.json +16 -2
- package/src/config/rule-stub-ceilings.json +133 -112
- package/src/config/turnaround-budget.json +66 -0
- package/src/install/claudeRuleRewrite.ts +288 -0
- package/src/install/detect.ts +60 -17
- package/src/install/detect_php_shape.ts +191 -0
- package/src/scripts/_cli/cmd_doctor.ts +27 -112
- package/src/scripts/_dispatch.bash +23 -0
- package/src/scripts/_lib/activation_ladder.ts +255 -0
- package/src/scripts/_lib/activation_receipt_producer.ts +341 -0
- package/src/scripts/_lib/activation_states.ts +88 -0
- package/src/scripts/_lib/adherence_detectors.ts +143 -0
- package/src/scripts/_lib/adr_frontmatter.ts +26 -0
- package/src/scripts/_lib/arith_claims.ts +181 -0
- package/src/scripts/_lib/arm_ranking.ts +62 -0
- package/src/scripts/_lib/asset_delivery_ledger.ts +303 -0
- package/src/scripts/_lib/audit_field_provenance.ts +54 -0
- package/src/scripts/_lib/auto_dispatch.ts +32 -3
- package/src/scripts/_lib/bench_ab_size_claim.ts +98 -4
- package/src/scripts/_lib/benchmark_cache_fields.ts +158 -0
- package/src/scripts/_lib/candidate_pair_delta.ts +349 -0
- package/src/scripts/_lib/candidate_proposer.ts +402 -0
- package/src/scripts/_lib/candidate_record.ts +601 -0
- package/src/scripts/_lib/capture_rate.ts +319 -0
- package/src/scripts/_lib/carrier_status.ts +118 -0
- package/src/scripts/_lib/cascade_stage_enumeration.ts +92 -0
- package/src/scripts/_lib/collector_denominator.ts +771 -0
- package/src/scripts/_lib/collector_record.ts +340 -0
- package/src/scripts/_lib/collector_store.ts +664 -0
- package/src/scripts/_lib/collector_supervision.ts +827 -0
- package/src/scripts/_lib/concern_estate.ts +77 -0
- package/src/scripts/_lib/config_chain.ts +477 -0
- package/src/scripts/_lib/config_cost.ts +209 -0
- package/src/scripts/_lib/corpus_manifest.ts +726 -0
- package/src/scripts/_lib/council_settings_block.ts +6 -0
- package/src/scripts/_lib/curator_ops.ts +172 -0
- package/src/scripts/_lib/delivery_arm_experiment.ts +282 -0
- package/src/scripts/_lib/delivery_criticality.ts +126 -0
- package/src/scripts/_lib/doctor_runtime_checks.ts +70 -0
- package/src/scripts/_lib/empty_cycles.ts +88 -0
- package/src/scripts/_lib/estate_carrier_cases.ts +72 -0
- package/src/scripts/_lib/eval_publication.ts +354 -0
- package/src/scripts/_lib/evaluation_cascade.ts +452 -0
- package/src/scripts/_lib/evaluation_vector.ts +283 -0
- package/src/scripts/_lib/evaluator_promotion.ts +225 -0
- package/src/scripts/_lib/evidence_basis.ts +118 -0
- package/src/scripts/_lib/evolution_roi.ts +562 -0
- package/src/scripts/_lib/exec_evidence.ts +8 -0
- package/src/scripts/_lib/experience_card.ts +272 -0
- package/src/scripts/_lib/experience_report.ts +151 -0
- package/src/scripts/_lib/experiment_freeze.ts +112 -0
- package/src/scripts/_lib/generated_by.ts +66 -0
- package/src/scripts/_lib/harness_evolution_guards.ts +239 -0
- package/src/scripts/_lib/hook_effect_probe.ts +141 -0
- package/src/scripts/_lib/host_denominator.ts +687 -0
- package/src/scripts/_lib/host_projection_reach.ts +157 -0
- package/src/scripts/_lib/ignored_blocker.ts +148 -0
- package/src/scripts/_lib/install_reach_checks.ts +495 -0
- package/src/scripts/_lib/json_duplicate_keys.ts +150 -0
- package/src/scripts/_lib/judge_hygiene.ts +228 -0
- package/src/scripts/_lib/legacy_boundary_map.ts +228 -0
- package/src/scripts/_lib/lexical_shortlist.ts +215 -0
- package/src/scripts/_lib/llm_candidate_proposer.ts +461 -0
- package/src/scripts/_lib/llm_proposer_transport.ts +153 -0
- package/src/scripts/_lib/loop_guards.ts +299 -0
- package/src/scripts/_lib/loss_class.ts +129 -0
- package/src/scripts/_lib/machine_wake.ts +51 -0
- package/src/scripts/_lib/md_prose_lines.ts +177 -0
- package/src/scripts/_lib/minimality_tiebreak.ts +168 -0
- package/src/scripts/_lib/one_resolver_invariant.ts +449 -0
- package/src/scripts/_lib/orchestration_record.ts +253 -5
- package/src/scripts/_lib/outcome_envelope.ts +227 -0
- package/src/scripts/_lib/outcome_vocabularies.ts +125 -0
- package/src/scripts/_lib/paired_stats.ts +245 -0
- package/src/scripts/_lib/paired_verdict.ts +226 -0
- package/src/scripts/_lib/pathology_archive.ts +393 -0
- package/src/scripts/_lib/prefix_stable_surfaces.ts +123 -0
- package/src/scripts/_lib/privacy_class.ts +47 -0
- package/src/scripts/_lib/promotion_capability.ts +315 -0
- package/src/scripts/_lib/promotion_evidence.ts +414 -0
- package/src/scripts/_lib/promotion_review.ts +294 -0
- package/src/scripts/_lib/prompt_shape.ts +92 -0
- package/src/scripts/_lib/regression_neighbourhood.ts +259 -0
- package/src/scripts/_lib/release_highlights.ts +70 -1
- package/src/scripts/_lib/release_material.ts +125 -2
- package/src/scripts/_lib/repeated_failure.ts +105 -0
- package/src/scripts/_lib/repo_root.ts +111 -0
- package/src/scripts/_lib/review_skipped_record.ts +37 -0
- package/src/scripts/_lib/role_split.ts +338 -0
- package/src/scripts/_lib/routing_corpus.ts +307 -0
- package/src/scripts/_lib/routing_index_input.ts +104 -0
- package/src/scripts/_lib/rule_injection.ts +22 -1
- package/src/scripts/_lib/runtime_journal.ts +1499 -0
- package/src/scripts/_lib/runtime_wiring_checks.ts +430 -0
- package/src/scripts/_lib/semantic_noop.ts +167 -0
- package/src/scripts/_lib/skill_catalogue.ts +11 -1
- package/src/scripts/_lib/source_digest.ts +200 -0
- package/src/scripts/_lib/source_redact.ts +260 -0
- package/src/scripts/_lib/source_shape.ts +502 -0
- package/src/scripts/_lib/source_snapshot_dedup.ts +199 -0
- package/src/scripts/_lib/strip_comments.d.mts +8 -0
- package/src/scripts/_lib/strip_comments.mjs +65 -0
- package/src/scripts/_lib/test_red_state.ts +154 -0
- package/src/scripts/_lib/trigger_shift.ts +144 -0
- package/src/scripts/_lib/untrusted_content.ts +82 -7
- package/src/scripts/activation_receipt.ts +258 -0
- package/src/scripts/adoption_report.ts +1 -1
- package/src/scripts/adr/evidence_census.ts +1 -1
- package/src/scripts/adr/regenerate_index.ts +26 -2
- package/src/scripts/adr_cite_check.ts +164 -1
- package/src/scripts/ai_council/blind_review.ts +11 -1
- package/src/scripts/ai_council/clients.ts +4 -0
- package/src/scripts/ai_council/config.ts +3 -0
- package/src/scripts/ai_council/consensus.ts +28 -8
- package/src/scripts/ai_council/consensus_round.ts +226 -0
- package/src/scripts/ai_council/cross_exam.ts +128 -0
- package/src/scripts/ai_council/disagreement_signal.ts +338 -0
- package/src/scripts/ai_council/explain_route.ts +157 -0
- package/src/scripts/ai_council/information_gain.ts +293 -0
- package/src/scripts/ai_council/inline_findings.ts +340 -0
- package/src/scripts/ai_council/judge_position_bias.ts +197 -0
- package/src/scripts/ai_council/leakage_corpus.ts +385 -0
- package/src/scripts/ai_council/leakage_patterns.ts +453 -0
- package/src/scripts/ai_council/minority_retention.ts +171 -0
- package/src/scripts/ai_council/orchestrator.ts +16 -203
- package/src/scripts/ai_council/orchestrator_results.ts +23 -3
- package/src/scripts/ai_council/pricing.ts +106 -0
- package/src/scripts/ai_council/prompts.ts +74 -7
- package/src/scripts/ai_council/provider_leakage_bench.ts +327 -0
- package/src/scripts/ai_council/py_format.ts +14 -3
- package/src/scripts/ai_council/recouncil_guard.ts +293 -0
- package/src/scripts/ai_council/recouncil_savings.ts +299 -0
- package/src/scripts/ai_council/replay_route.ts +223 -0
- package/src/scripts/ai_council/response_record.ts +50 -0
- package/src/scripts/ai_council/reviewer_assignment.ts +279 -0
- package/src/scripts/ai_council/routing_training_row.ts +197 -0
- package/src/scripts/ai_council/synthesis_strategy.ts +159 -0
- package/src/scripts/ai_council/topology_bench_manifest.ts +860 -0
- package/src/scripts/ai_council/topology_vocabulary.ts +175 -0
- package/src/scripts/asset_delivery_ledger.ts +157 -0
- package/src/scripts/attest_artifact.ts +193 -0
- package/src/scripts/audit_auto_rules.ts +2 -1
- package/src/scripts/audit_command_surface.ts +1 -1
- package/src/scripts/audit_mcp_tools.ts +2 -2
- package/src/scripts/audit_skill_overlap_allowlist.json +6 -1
- package/src/scripts/audit_user_type_axis.ts +2 -1
- package/src/scripts/bench_ab_clone.ts +215 -10
- package/src/scripts/bench_ab_integrity.ts +119 -5
- package/src/scripts/bench_ab_v2_stats.ts +40 -213
- package/src/scripts/build_archive_index.ts +1 -1
- package/src/scripts/build_linear_digest.ts +1 -1
- package/src/scripts/build_proof.ts +11 -4
- package/src/scripts/build_rule_trigger_matrix.ts +1 -1
- package/src/scripts/build_source_digests.ts +157 -0
- package/src/scripts/cache_realization_report.ts +103 -0
- package/src/scripts/check_adr_frontmatter.ts +131 -10
- package/src/scripts/check_benchmark_report_fields.ts +207 -0
- package/src/scripts/check_beta_review_markers.ts +235 -6
- package/src/scripts/check_bundle_path_leakage.ts +34 -1
- package/src/scripts/check_claims.ts +576 -6
- package/src/scripts/check_composite_arming.ts +35 -0
- package/src/scripts/check_concern_admissions.ts +365 -0
- package/src/scripts/check_enforcement_coverage.ts +28 -0
- package/src/scripts/check_estate_count.ts +347 -25
- package/src/scripts/check_gate_reachability.ts +383 -0
- package/src/scripts/check_host_loadability.ts +104 -0
- package/src/scripts/check_loss_class_declared.ts +308 -0
- package/src/scripts/check_md_language.ts +15 -98
- package/src/scripts/check_memory_proposal.ts +18 -11
- package/src/scripts/check_no_currency_in_cost_surfaces.ts +225 -0
- package/src/scripts/check_no_external_sources.ts +281 -6
- package/src/scripts/check_pack_conformance_fixture.ts +11 -1
- package/src/scripts/check_pack_size.ts +59 -2
- package/src/scripts/check_preamble_payload_budget.ts +114 -3
- package/src/scripts/check_prefix_stable_mutation.ts +372 -0
- package/src/scripts/check_publish_surface.ts +10 -1
- package/src/scripts/check_references.ts +24 -1
- package/src/scripts/check_release_highlights.ts +96 -13
- package/src/scripts/check_roadmap_trackable.ts +17 -2
- package/src/scripts/check_rule_evidence_declaration.ts +316 -0
- package/src/scripts/check_rule_projection_integrity.ts +14 -4
- package/src/scripts/check_score_contract.ts +26 -3
- package/src/scripts/check_static_parity.ts +285 -0
- package/src/scripts/check_supervision_claim_atomicity.ts +498 -0
- package/src/scripts/check_suppression_hygiene.ts +15 -0
- package/src/scripts/ci_settle.ts +39 -2
- package/src/scripts/code_graph/build.ts +60 -3
- package/src/scripts/code_graph/extract.ts +44 -0
- package/src/scripts/code_graph/types.ts +24 -0
- package/src/scripts/collector_daemon.ts +714 -0
- package/src/scripts/config_cost_report.ts +302 -0
- package/src/scripts/consumer_matrix.ts +74 -1
- package/src/scripts/corpus_manifest.ts +235 -0
- package/src/scripts/cost/budget.mjs +10 -3
- package/src/scripts/cost/track.mjs +10 -3
- package/src/scripts/council_cli.ts +37 -54
- package/src/scripts/dispatch_r2_reviewer.ts +46 -5
- package/src/scripts/evolution_lab.ts +1153 -0
- package/src/scripts/external_sources_denylist.json +5 -18
- package/src/scripts/extract_audit_patterns.ts +21 -0
- package/src/scripts/fold_intake.ts +12 -1
- package/src/scripts/gen_discovery_baseline.ts +1 -1
- package/src/scripts/generate_capabilities_index.ts +12 -5
- package/src/scripts/generate_capability_matrix.ts +3 -3
- package/src/scripts/generate_command_flows.ts +2 -2
- package/src/scripts/generate_cookbook.ts +2 -2
- package/src/scripts/generate_index.ts +8 -1
- package/src/scripts/generate_knowledge_index.ts +2 -1
- package/src/scripts/generate_ownership_matrix.ts +2 -1
- package/src/scripts/generate_pack_manifests.ts +10 -2
- package/src/scripts/generate_role_experiences_catalog.ts +2 -2
- package/src/scripts/generate_settings_reference.ts +1 -1
- package/src/scripts/git_authorization_hook.ts +22 -1
- package/src/scripts/grade_target_readiness.ts +48 -5
- package/src/scripts/hook_effect_doctor.ts +261 -0
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +125 -16
- package/src/scripts/hooks/block_speaking_inbox_dir.ts +271 -0
- package/src/scripts/hooks/block_unauthorized_git.ts +18 -0
- package/src/scripts/hooks/comment_discipline_hook.ts +146 -0
- package/src/scripts/hooks/concern_registry.ts +6 -0
- package/src/scripts/hooks/dispatch_hook.ts +32 -0
- package/src/scripts/hooks/journal_record_hook.ts +345 -0
- package/src/scripts/hooks/prepush_metadata_sources.sh +110 -0
- package/src/scripts/hooks/run_continuation_hook.ts +44 -9
- package/src/scripts/hooks/shims/install.sh +61 -0
- package/src/scripts/hooks/shims/php +101 -0
- package/src/scripts/hooks/suggestion_capture_hook.ts +10 -0
- package/src/scripts/hooks/turn_end_gate_hook.ts +9 -1
- package/src/scripts/hot_context_hook.ts +16 -0
- package/src/scripts/install-hooks.sh +20 -5
- package/src/scripts/install.ts +17 -35
- package/src/scripts/inventory_frontmatter.ts +2 -1
- package/src/scripts/language_mirror_hook.ts +14 -2
- package/src/scripts/lint_adr_sweep_routing.ts +12 -0
- package/src/scripts/lint_canonical_terms.ts +604 -0
- package/src/scripts/lint_carrier_integrity.ts +820 -0
- package/src/scripts/lint_code_comments.ts +643 -0
- package/src/scripts/lint_composition_review.ts +619 -0
- package/src/scripts/lint_consolidation_lineage.ts +666 -0
- package/src/scripts/lint_eval_specs.ts +593 -0
- package/src/scripts/lint_hook_manifest.ts +19 -0
- package/src/scripts/lint_host_portability.ts +373 -0
- package/src/scripts/lint_judge_prompt_expectation.ts +27 -1
- package/src/scripts/lint_memory_twin_parity.ts +335 -0
- package/src/scripts/lint_metric_consumers.ts +355 -0
- package/src/scripts/lint_plan_risk_register.ts +39 -19
- package/src/scripts/lint_promotion_paths.ts +779 -0
- package/src/scripts/lint_skill_descriptions.ts +18 -1
- package/src/scripts/lint_skill_frontmatter_safety.ts +58 -0
- package/src/scripts/lint_skill_link_reach.ts +252 -0
- package/src/scripts/lint_skill_trigger_corpus.ts +426 -23
- package/src/scripts/lint_ticket_buildable.ts +17 -1
- package/src/scripts/llm_propose.ts +256 -0
- package/src/scripts/mcp_server/consumer_tool_catalog.json +1 -1
- package/src/scripts/mcp_server/tool_catalog_source.ts +1 -1
- package/src/scripts/measure_delivery_sets.ts +361 -0
- package/src/scripts/measure_docs_dead_links.ts +348 -0
- package/src/scripts/measure_host_capture.ts +461 -0
- package/src/scripts/measure_routing_signal.ts +307 -0
- package/src/scripts/migration_status.ts +3 -3
- package/src/scripts/model_rule_injection.ts +76 -0
- package/src/scripts/orchestration_record.ts +14 -0
- package/src/scripts/preamble_byte_census.ts +139 -10
- package/src/scripts/prepack-check.mjs +4 -1
- package/src/scripts/probe_council_retention.ts +495 -0
- package/src/scripts/probe_turnaround.ts +384 -0
- package/src/scripts/probe_unblocked_ask.ts +279 -0
- package/src/scripts/release.ts +21 -37
- package/src/scripts/release_drill.ts +143 -0
- package/src/scripts/release_env.ts +35 -0
- package/src/scripts/release_publication.ts +84 -0
- package/src/scripts/render_benchmark_md.ts +1 -1
- package/src/scripts/rule_activation_census.ts +70 -9
- package/src/scripts/rule_backlinks.ts +1 -1
- package/src/scripts/run_lifecycle_suite.ts +208 -0
- package/src/scripts/run_skill_evals.ts +40 -0
- package/src/scripts/schemas/delivery-manifest.schema.json +43 -0
- package/src/scripts/schemas/drafting-channels.schema.json +43 -0
- package/src/scripts/schemas/evals.schema.json +24 -1
- package/src/scripts/schemas/rule.schema.json +52 -1
- package/src/scripts/schemas/skill.schema.json +17 -2
- package/src/scripts/skill_overlap.ts +7 -1
- package/src/scripts/skill_tools/run_block_d_eval.ts +8 -1
- package/src/scripts/skill_tools/score_skill_relevance.ts +84 -3
- package/src/scripts/skill_tools/suggest_skill_for_task.ts +8 -1
- package/src/scripts/skill_usage_report.ts +2 -1
- package/src/scripts/stubs_due.ts +211 -0
- package/src/scripts/sweep_source_surfaces.ts +678 -0
- package/src/scripts/sync_pr_branch.ts +3 -3
- package/src/scripts/trigger_eval_grandfather.json +202 -222
- package/src/scripts/utilization_report.ts +22 -2
- package/src/server/schemas/settings.ts +5 -0
- package/src/templates/marketing-copy.yml +1 -1
- package/dist/ui/assets/index-Cxgy63kz.js +0 -26
|
@@ -0,0 +1,687 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The host-emitted-event denominator — a typed, privacy-bounded count
|
|
3
|
+
* reconstructed from a HOST-authored artefact.
|
|
4
|
+
*
|
|
5
|
+
* `road-to-journal-host-capture-measurement` Phase 2 step 2.1 and AC-2. The
|
|
6
|
+
* survey of step 1.1
|
|
7
|
+
* (`agents/evidence/analysis/host-denominator-obtainability-2026-08-29.md`)
|
|
8
|
+
* established that exactly six `(platform, event)` cells carry an obtainable
|
|
9
|
+
* host denominator, all on `claude`, all reconstructable from the per-session
|
|
10
|
+
* transcript Claude Code writes at
|
|
11
|
+
* `~/.claude/projects/<project-slug>/<session-id>.jsonl`.
|
|
12
|
+
*
|
|
13
|
+
* ## Why this module exists rather than a one-off script
|
|
14
|
+
*
|
|
15
|
+
* AC-2 requires the denominator's record type to be *asserted against a
|
|
16
|
+
* committed key set, with a free-form write failing to type-check* — the same
|
|
17
|
+
* privacy property `_lib/runtime_journal.ts` carries for the numerator. A
|
|
18
|
+
* denominator computed inline in a script has no record type to assert, so the
|
|
19
|
+
* two halves of the capture rate would have been held to different standards.
|
|
20
|
+
* They are held to the same one here: {@link DENOMINATOR_RECORD_KEYS} is bound
|
|
21
|
+
* to {@link HostDenominator} in both directions, and the record is passed
|
|
22
|
+
* through the journal's own `NoFreeForm` guard rather than a local copy of it.
|
|
23
|
+
*
|
|
24
|
+
* ## The scope must match the numerator's, and v1 did not — R2 finding 1
|
|
25
|
+
*
|
|
26
|
+
* The numerator is ONE repository's journal (`<git-common-dir>/agent-journal/`,
|
|
27
|
+
* shared across that repository's worktrees and reaching nothing else). v1 of
|
|
28
|
+
* this module walked every project directory on the machine, so the ratio was
|
|
29
|
+
* over two different populations. It was invisible in the first published run
|
|
30
|
+
* only because the numerator was `store-absent`, and the evidence page's own
|
|
31
|
+
* `Revisit-if` told the next reader to re-run the script on a machine where it
|
|
32
|
+
* would not have been.
|
|
33
|
+
*
|
|
34
|
+
* {@link DenominatorScope} makes the scope an explicit, recorded field, and
|
|
35
|
+
* `repository` is the default: a rate needs one population, and the numerator's
|
|
36
|
+
* is the one that cannot be widened. `machine` remains available because a
|
|
37
|
+
* machine-wide denominator is a legitimate *different* figure — it just may
|
|
38
|
+
* never be divided by a repository-scoped numerator.
|
|
39
|
+
*
|
|
40
|
+
* ## The reconstruction is a stated rule set, not a heuristic
|
|
41
|
+
*
|
|
42
|
+
* {@link RECONSTRUCTION_RULE_VERSION} pins the rules, and the pinning matters:
|
|
43
|
+
* a denominator whose derivation can be adjusted after the numerator is known
|
|
44
|
+
* is not a denominator. v2 states two rules v1 left implicit — the sidechain
|
|
45
|
+
* treatment of *assistant* records, which governs 97 % of the count, and the
|
|
46
|
+
* direction in which the `session_start` rule fails.
|
|
47
|
+
*
|
|
48
|
+
* ## What this module refuses to count, and why that is the interesting half
|
|
49
|
+
*
|
|
50
|
+
* `stop` is journal-bound on `claude` and would widen the measurement from
|
|
51
|
+
* five journal-bound cells to six. Three candidate denominators for it were
|
|
52
|
+
* examined and **all three are refused**; {@link STOP_CANDIDATES} records them
|
|
53
|
+
* with the reading that refuses each, and they disagree by a factor of about 44
|
|
54
|
+
* (305 / 7). A `stop` denominator chosen from among three mutually inconsistent
|
|
55
|
+
* candidates would be a number with a footnote where the footnote is
|
|
56
|
+
* load-bearing.
|
|
57
|
+
*/
|
|
58
|
+
|
|
59
|
+
import { execFileSync } from 'node:child_process';
|
|
60
|
+
import * as fs from 'node:fs';
|
|
61
|
+
import * as path from 'node:path';
|
|
62
|
+
import * as readline from 'node:readline';
|
|
63
|
+
|
|
64
|
+
import type { NoFreeForm } from './runtime_journal.js';
|
|
65
|
+
|
|
66
|
+
type Assert<T extends true> = T;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Bumped whenever any rule in {@link RECONSTRUCTION_RULES} changes. A published
|
|
70
|
+
* rate cites this number, so a later reader can tell a re-measurement from a
|
|
71
|
+
* re-derivation.
|
|
72
|
+
*
|
|
73
|
+
* - **v1** (2026-08-29) — the 1.1 survey's four rules plus the `isMeta` and
|
|
74
|
+
* `isSidechain` refinements on `user` records.
|
|
75
|
+
* - **v2** (2026-08-29, after R2 review) — states the sidechain rule for
|
|
76
|
+
* *assistant* records, which v1 left implicit while it governed 97 % of the
|
|
77
|
+
* count; states the direction in which the `session_start` rule fails; and
|
|
78
|
+
* takes `first_at` as the MINIMUM timestamp rather than the first one in file
|
|
79
|
+
* order.
|
|
80
|
+
*/
|
|
81
|
+
export const RECONSTRUCTION_RULE_VERSION = 2;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Which population the denominator is over.
|
|
85
|
+
*
|
|
86
|
+
* `repository` — only transcripts whose project directory belongs to the
|
|
87
|
+
* measured repository or one of its worktrees. This is the scope the
|
|
88
|
+
* repository-scoped journal numerator can actually reach, and it is the default
|
|
89
|
+
* for that reason.
|
|
90
|
+
*
|
|
91
|
+
* `machine` — every transcript on the machine. A legitimate figure about the
|
|
92
|
+
* host, and NOT divisible by a repository-scoped numerator.
|
|
93
|
+
*/
|
|
94
|
+
export const DENOMINATOR_SCOPES = ['repository', 'machine'] as const;
|
|
95
|
+
export type DenominatorScope = (typeof DENOMINATOR_SCOPES)[number];
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* The six `(claude, event)` cells the 1.1 survey found `counted`. This is the
|
|
99
|
+
* measurement's scope and no more: a rate over six of 43 bound cells is a
|
|
100
|
+
* different claim from a rate over all of them.
|
|
101
|
+
*/
|
|
102
|
+
export const COUNTED_EVENTS = [
|
|
103
|
+
'session_start',
|
|
104
|
+
'user_prompt_submit',
|
|
105
|
+
'pre_tool_use',
|
|
106
|
+
'post_tool_use',
|
|
107
|
+
'subagent_start',
|
|
108
|
+
'subagent_stop',
|
|
109
|
+
] as const;
|
|
110
|
+
|
|
111
|
+
export type CountedEvent = (typeof COUNTED_EVENTS)[number];
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Of the six, the five where `journal-record` is actually bound in
|
|
115
|
+
* `src/scripts/hook_manifest.yaml` on the `claude` platform.
|
|
116
|
+
*
|
|
117
|
+
* `pre_tool_use` is the omission and it is deliberate rather than an oversight
|
|
118
|
+
* to be corrected here: that slot carries the safety guards, and the journal is
|
|
119
|
+
* not among them. Its numerator is therefore zero **by construction**, which is
|
|
120
|
+
* a different fact from zero-because-nothing-fired, and the published table
|
|
121
|
+
* separates the two instead of averaging them into one rate.
|
|
122
|
+
*/
|
|
123
|
+
export const JOURNAL_BOUND_COUNTED_EVENTS = [
|
|
124
|
+
'session_start',
|
|
125
|
+
'user_prompt_submit',
|
|
126
|
+
'post_tool_use',
|
|
127
|
+
'subagent_start',
|
|
128
|
+
'subagent_stop',
|
|
129
|
+
] as const;
|
|
130
|
+
|
|
131
|
+
type _BoundIsSubsetOfCounted = Assert<
|
|
132
|
+
Exclude<(typeof JOURNAL_BOUND_COUNTED_EVENTS)[number], CountedEvent> extends never ? true : false
|
|
133
|
+
>;
|
|
134
|
+
|
|
135
|
+
/** Human-readable statement of every rule the reconstruction applies. */
|
|
136
|
+
export const RECONSTRUCTION_RULES: Readonly<Record<string, string>> = Object.freeze({
|
|
137
|
+
session_start:
|
|
138
|
+
'One per transcript file. NOT an iff, and v1 claimed it was: the host fires `SessionStart` again on resume, clear and compact while a resumed session appends to the SAME file (under-count), and a compacted or forked session can produce a SECOND file for one logical session (over-count on the population side). Both directions are present and neither is corrected, so this cell is the least precise of the six and the population figure derived from it inherits that.',
|
|
139
|
+
user_prompt_submit:
|
|
140
|
+
'A `type: "user"` record carrying no `tool_result` content block. REFINEMENT 1: records with `isMeta: true` are excluded — an injected system reminder is not a user prompt submit. REFINEMENT 2: records with `isSidechain: true` are excluded — a subagent brief does not fire the host event in the parent session. The two exclusion counters are INDEPENDENT: a record carrying both flags increments both, so they may sum to more than the number of records excluded.',
|
|
141
|
+
pre_tool_use:
|
|
142
|
+
'One per `tool_use` content block in a `type: "assistant"` record. Sidechain records ARE INCLUDED — a subagent tool call fires the parent session tool hooks — and the sidechain share is counted separately in `sidechain_tool_use_blocks` so a reader who disagrees can subtract it. v1 left this rule unstated while it governed about 97 % of the six-cell total, which is why it is written here rather than inferred from the code.',
|
|
143
|
+
post_tool_use:
|
|
144
|
+
'One per `tool_use` content block in a `type: "assistant"` record. Same sidechain treatment, same separately-published share.',
|
|
145
|
+
subagent_start:
|
|
146
|
+
'One per `tool_use` content block whose `name` is `Agent` or `Task`. A subset of the tool_use blocks, and a distinct host event. Sidechain-nested spawns are included and counted in `sidechain_agent_tool_use_blocks`.',
|
|
147
|
+
subagent_stop:
|
|
148
|
+
'One per `tool_use` content block whose `name` is `Agent` or `Task`. Same treatment as `subagent_start`.',
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* The three refused candidates for a `stop` denominator, each with the reading
|
|
153
|
+
* that refuses it. Kept in code rather than only in prose so a later attempt
|
|
154
|
+
* meets the measurement before it meets the idea.
|
|
155
|
+
*
|
|
156
|
+
* All three readings are from one transcript,
|
|
157
|
+
* `b818ccad-3581-4773-b9a2-c7272af04ce2`, and they disagree by a factor of
|
|
158
|
+
* about 44 (305 / 7 = 43.6).
|
|
159
|
+
*/
|
|
160
|
+
export const STOP_CANDIDATES: readonly Readonly<{
|
|
161
|
+
candidate: string;
|
|
162
|
+
reading: number;
|
|
163
|
+
refused_because: string;
|
|
164
|
+
}>[] = Object.freeze([
|
|
165
|
+
Object.freeze({
|
|
166
|
+
candidate: 'assistant records carrying a `stop_reason`',
|
|
167
|
+
reading: 305,
|
|
168
|
+
refused_because:
|
|
169
|
+
'counts assistant MESSAGES, not turn completions — every one read `stop_reason: "tool_use"` and none `end_turn`, so it over-counts by about two orders of magnitude (the 1.1 survey established this).',
|
|
170
|
+
}),
|
|
171
|
+
Object.freeze({
|
|
172
|
+
candidate: '`type: "last-prompt"` records',
|
|
173
|
+
reading: 95,
|
|
174
|
+
refused_because:
|
|
175
|
+
'written once per assistant leaf and repeating the SAME prompt text, so it is a per-turn marker whose mapping onto the `stop` hook event is unverified rather than a published count.',
|
|
176
|
+
}),
|
|
177
|
+
Object.freeze({
|
|
178
|
+
candidate: '`hookInfos` entries whose command carries `--event stop`',
|
|
179
|
+
reading: 7,
|
|
180
|
+
refused_because:
|
|
181
|
+
'host-authored and event-named — the strongest of the three — but it appears on only 7 of about 95 turns in the same session, so the host records these entries selectively (they co-occur with `hookAdditionalContext` / `preventedContinuation`) and the artefact UNDER-counts dispatches.',
|
|
182
|
+
}),
|
|
183
|
+
]);
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* One denominator reading. These fields and no others.
|
|
187
|
+
*
|
|
188
|
+
* Every field is a bounded scalar: a count, an ISO calendar date, a scope
|
|
189
|
+
* literal, or the one platform literal the survey found countable. Nothing here
|
|
190
|
+
* can hold a path, a project name, a session id, a prompt, or a tool name — the
|
|
191
|
+
* same property the numerator's record carries, asserted the same way.
|
|
192
|
+
*/
|
|
193
|
+
export interface HostDenominator {
|
|
194
|
+
/** The only platform with an obtainable host denominator (1.1 survey). */
|
|
195
|
+
platform: 'claude';
|
|
196
|
+
/** Which population the counts are over. See {@link DenominatorScope}. */
|
|
197
|
+
scope: DenominatorScope;
|
|
198
|
+
/** Which rule set produced the counts. See {@link RECONSTRUCTION_RULE_VERSION}. */
|
|
199
|
+
reconstruction_rule_version: number;
|
|
200
|
+
/** Inclusive window start, ISO calendar date (`YYYY-MM-DD`). */
|
|
201
|
+
window_start: string;
|
|
202
|
+
/** Inclusive window end, ISO calendar date (`YYYY-MM-DD`). */
|
|
203
|
+
window_end: string;
|
|
204
|
+
/** Transcripts inside the scope, before windowing. */
|
|
205
|
+
transcripts_found: number;
|
|
206
|
+
/** Project directories skipped because they are outside the scope. */
|
|
207
|
+
transcripts_out_of_scope: number;
|
|
208
|
+
/** Transcripts whose earliest record falls inside the window. */
|
|
209
|
+
sessions_in_window: number;
|
|
210
|
+
/** Transcripts whose earliest record predates the window. */
|
|
211
|
+
sessions_before_window: number;
|
|
212
|
+
/** Transcripts whose earliest record postdates the window. */
|
|
213
|
+
sessions_after_window: number;
|
|
214
|
+
/** Transcripts carrying no parseable timestamped record at all. */
|
|
215
|
+
sessions_undatable: number;
|
|
216
|
+
session_start: number;
|
|
217
|
+
user_prompt_submit: number;
|
|
218
|
+
pre_tool_use: number;
|
|
219
|
+
post_tool_use: number;
|
|
220
|
+
subagent_start: number;
|
|
221
|
+
subagent_stop: number;
|
|
222
|
+
/** `user` records excluded by refinement 1 (`isMeta`). Audit trail. */
|
|
223
|
+
excluded_meta_user_records: number;
|
|
224
|
+
/** `user` records excluded by refinement 2 (`isSidechain`). Audit trail. */
|
|
225
|
+
excluded_sidechain_user_records: number;
|
|
226
|
+
/** Of `pre_tool_use` / `post_tool_use`, how many came from sidechain records. */
|
|
227
|
+
sidechain_tool_use_blocks: number;
|
|
228
|
+
/** Of `subagent_start` / `subagent_stop`, how many came from sidechain records. */
|
|
229
|
+
sidechain_agent_tool_use_blocks: number;
|
|
230
|
+
/** Directories the walk could not read. Never silently zero. */
|
|
231
|
+
unreadable_directories: number;
|
|
232
|
+
/** Transcript lines that did not parse as JSON. Never silently discarded. */
|
|
233
|
+
unparseable_lines: number;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** The committed key set. AC-2 asserts a record's keys against exactly this. */
|
|
237
|
+
export const DENOMINATOR_RECORD_KEYS = Object.freeze([
|
|
238
|
+
'platform',
|
|
239
|
+
'scope',
|
|
240
|
+
'reconstruction_rule_version',
|
|
241
|
+
'window_start',
|
|
242
|
+
'window_end',
|
|
243
|
+
'transcripts_found',
|
|
244
|
+
'transcripts_out_of_scope',
|
|
245
|
+
'sessions_in_window',
|
|
246
|
+
'sessions_before_window',
|
|
247
|
+
'sessions_after_window',
|
|
248
|
+
'sessions_undatable',
|
|
249
|
+
'session_start',
|
|
250
|
+
'user_prompt_submit',
|
|
251
|
+
'pre_tool_use',
|
|
252
|
+
'post_tool_use',
|
|
253
|
+
'subagent_start',
|
|
254
|
+
'subagent_stop',
|
|
255
|
+
'excluded_meta_user_records',
|
|
256
|
+
'excluded_sidechain_user_records',
|
|
257
|
+
'sidechain_tool_use_blocks',
|
|
258
|
+
'sidechain_agent_tool_use_blocks',
|
|
259
|
+
'unreadable_directories',
|
|
260
|
+
'unparseable_lines',
|
|
261
|
+
] as const);
|
|
262
|
+
|
|
263
|
+
type _KeysCoverTheRecord = Assert<
|
|
264
|
+
Exclude<keyof HostDenominator, (typeof DENOMINATOR_RECORD_KEYS)[number]> extends never
|
|
265
|
+
? true
|
|
266
|
+
: false
|
|
267
|
+
>;
|
|
268
|
+
type _KeysAddNothing = Assert<
|
|
269
|
+
Exclude<(typeof DENOMINATOR_RECORD_KEYS)[number], keyof HostDenominator> extends never
|
|
270
|
+
? true
|
|
271
|
+
: false
|
|
272
|
+
>;
|
|
273
|
+
/**
|
|
274
|
+
* Fails to compile if {@link HostDenominator} ever grows a key from the
|
|
275
|
+
* journal's `FREE_FORM_KEYS` — the numerator's guard, reused rather than
|
|
276
|
+
* re-implemented, so the two halves cannot drift apart. The list itself is not
|
|
277
|
+
* imported as a value here: only the type-level guard is needed, and the test
|
|
278
|
+
* asserts the runtime mirror against the journal's own export directly.
|
|
279
|
+
*/
|
|
280
|
+
type _RecordCarriesNoFreeFormField = Assert<
|
|
281
|
+
[NoFreeForm<HostDenominator>] extends [never] ? false : true
|
|
282
|
+
>;
|
|
283
|
+
|
|
284
|
+
/** Raised when a record is handed to {@link validateDenominator} and fails it. */
|
|
285
|
+
export class DenominatorContractError extends Error {}
|
|
286
|
+
|
|
287
|
+
/** Keys that are not counts. Everything else must be a non-negative integer. */
|
|
288
|
+
const NON_COUNT_KEYS = new Set<string>(['platform', 'scope', 'window_start', 'window_end']);
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Runtime half of the schema guard: an unknown key is **REJECTED, not
|
|
292
|
+
* dropped**, on the same reasoning `_lib/collector_record.ts` states — a
|
|
293
|
+
* producer whose extra field is silently discarded has been told the field is
|
|
294
|
+
* fine, and the leak then lives upstream where this schema cannot see it.
|
|
295
|
+
*/
|
|
296
|
+
export function validateDenominator(record: Record<string, unknown>): HostDenominator {
|
|
297
|
+
const allowed = new Set<string>(DENOMINATOR_RECORD_KEYS);
|
|
298
|
+
for (const key of Object.keys(record)) {
|
|
299
|
+
if (!allowed.has(key)) {
|
|
300
|
+
throw new DenominatorContractError(`unknown field '${key}' — REJECTED, not dropped`);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
for (const key of DENOMINATOR_RECORD_KEYS) {
|
|
304
|
+
if (!(key in record)) {
|
|
305
|
+
throw new DenominatorContractError(`missing field '${key}'`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
if (record.platform !== 'claude') {
|
|
309
|
+
throw new DenominatorContractError(
|
|
310
|
+
`platform must be 'claude' — the only cell set with an obtainable host denominator`,
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
if (!(DENOMINATOR_SCOPES as readonly string[]).includes(record.scope as string)) {
|
|
314
|
+
throw new DenominatorContractError(
|
|
315
|
+
`scope must be one of ${DENOMINATOR_SCOPES.join(' | ')} — an unrecorded scope is how ` +
|
|
316
|
+
`a denominator ends up over a different population than its numerator`,
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
for (const key of DENOMINATOR_RECORD_KEYS) {
|
|
320
|
+
if (NON_COUNT_KEYS.has(key)) continue;
|
|
321
|
+
const value = record[key];
|
|
322
|
+
if (typeof value !== 'number' || !Number.isInteger(value) || value < 0) {
|
|
323
|
+
throw new DenominatorContractError(`field '${key}' must be a non-negative integer`);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
for (const key of ['window_start', 'window_end'] as const) {
|
|
327
|
+
const value = record[key];
|
|
328
|
+
if (typeof value !== 'string' || !/^\d{4}-\d{2}-\d{2}$/.test(value)) {
|
|
329
|
+
throw new DenominatorContractError(
|
|
330
|
+
`field '${key}' must be an ISO calendar date (YYYY-MM-DD) — a per-second timestamp beside a session count reconstructs working hours`,
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
if ((record.window_start as string) > (record.window_end as string)) {
|
|
335
|
+
throw new DenominatorContractError(
|
|
336
|
+
`window_start '${String(record.window_start)}' is after window_end ` +
|
|
337
|
+
`'${String(record.window_end)}' — an inverted window files every transcript as ` +
|
|
338
|
+
`out-of-window and reports a zero denominator instead of a bad input`,
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
return record as unknown as HostDenominator;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/** A zeroed record for the given scope and window. */
|
|
345
|
+
export function emptyDenominator(
|
|
346
|
+
windowStart: string,
|
|
347
|
+
windowEnd: string,
|
|
348
|
+
scope: DenominatorScope = 'repository',
|
|
349
|
+
): HostDenominator {
|
|
350
|
+
return validateDenominator({
|
|
351
|
+
platform: 'claude',
|
|
352
|
+
scope,
|
|
353
|
+
reconstruction_rule_version: RECONSTRUCTION_RULE_VERSION,
|
|
354
|
+
window_start: windowStart,
|
|
355
|
+
window_end: windowEnd,
|
|
356
|
+
transcripts_found: 0,
|
|
357
|
+
transcripts_out_of_scope: 0,
|
|
358
|
+
sessions_in_window: 0,
|
|
359
|
+
sessions_before_window: 0,
|
|
360
|
+
sessions_after_window: 0,
|
|
361
|
+
sessions_undatable: 0,
|
|
362
|
+
session_start: 0,
|
|
363
|
+
user_prompt_submit: 0,
|
|
364
|
+
pre_tool_use: 0,
|
|
365
|
+
post_tool_use: 0,
|
|
366
|
+
subagent_start: 0,
|
|
367
|
+
subagent_stop: 0,
|
|
368
|
+
excluded_meta_user_records: 0,
|
|
369
|
+
excluded_sidechain_user_records: 0,
|
|
370
|
+
sidechain_tool_use_blocks: 0,
|
|
371
|
+
sidechain_agent_tool_use_blocks: 0,
|
|
372
|
+
unreadable_directories: 0,
|
|
373
|
+
unparseable_lines: 0,
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/** Per-transcript counts, before windowing folds them into a total. */
|
|
378
|
+
export interface TranscriptCounts {
|
|
379
|
+
/**
|
|
380
|
+
* MINIMUM record timestamp in the file, not the first one in file order.
|
|
381
|
+
* A transcript whose leading record is back-dated would otherwise be
|
|
382
|
+
* windowed by that record's day, which decides whether the whole file's
|
|
383
|
+
* counts land in the denominator at all (R2 finding 12).
|
|
384
|
+
*/
|
|
385
|
+
first_at: string | null;
|
|
386
|
+
user_prompt_submit: number;
|
|
387
|
+
tool_use_blocks: number;
|
|
388
|
+
agent_tool_use_blocks: number;
|
|
389
|
+
sidechain_tool_use_blocks: number;
|
|
390
|
+
sidechain_agent_tool_use_blocks: number;
|
|
391
|
+
excluded_meta_user_records: number;
|
|
392
|
+
excluded_sidechain_user_records: number;
|
|
393
|
+
unparseable_lines: number;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Apply {@link RECONSTRUCTION_RULES} to one transcript's records.
|
|
398
|
+
*
|
|
399
|
+
* Pure: takes already-parsed records, touches no filesystem, so the rules are
|
|
400
|
+
* testable against inline fixtures and the published counts are reproducible
|
|
401
|
+
* without the measured machine's `~/.claude`.
|
|
402
|
+
*/
|
|
403
|
+
export function countTranscript(records: readonly Record<string, unknown>[]): TranscriptCounts {
|
|
404
|
+
const out = emptyTranscriptCounts();
|
|
405
|
+
for (const record of records) foldRecord(out, record);
|
|
406
|
+
return out;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/** A zeroed per-transcript accumulator. */
|
|
410
|
+
export function emptyTranscriptCounts(): TranscriptCounts {
|
|
411
|
+
return {
|
|
412
|
+
first_at: null,
|
|
413
|
+
user_prompt_submit: 0,
|
|
414
|
+
tool_use_blocks: 0,
|
|
415
|
+
agent_tool_use_blocks: 0,
|
|
416
|
+
sidechain_tool_use_blocks: 0,
|
|
417
|
+
sidechain_agent_tool_use_blocks: 0,
|
|
418
|
+
excluded_meta_user_records: 0,
|
|
419
|
+
excluded_sidechain_user_records: 0,
|
|
420
|
+
unparseable_lines: 0,
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Apply the rules to a single record. The one place the rules live — the
|
|
426
|
+
* whole-file and the streaming path both call it, so they cannot diverge.
|
|
427
|
+
*/
|
|
428
|
+
export function foldRecord(out: TranscriptCounts, record: Record<string, unknown>): void {
|
|
429
|
+
const at = record.timestamp;
|
|
430
|
+
if (typeof at === 'string' && at.length > 0) {
|
|
431
|
+
// MINIMUM, not first-seen. See TranscriptCounts.first_at.
|
|
432
|
+
if (out.first_at === null || at < out.first_at) out.first_at = at;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const sidechain = record.isSidechain === true;
|
|
436
|
+
|
|
437
|
+
if (record.type === 'user') {
|
|
438
|
+
// The two exclusion counters are INDEPENDENT: a record carrying both
|
|
439
|
+
// flags increments both. v1 tested `isSidechain` first and returned, so
|
|
440
|
+
// the two counts were published as independent while they were not
|
|
441
|
+
// (R2 finding 11).
|
|
442
|
+
if (sidechain) out.excluded_sidechain_user_records += 1;
|
|
443
|
+
if (record.isMeta === true) out.excluded_meta_user_records += 1;
|
|
444
|
+
if (sidechain || record.isMeta === true) return;
|
|
445
|
+
|
|
446
|
+
const content = (record.message as { content?: unknown } | undefined)?.content;
|
|
447
|
+
const carriesToolResult =
|
|
448
|
+
Array.isArray(content) &&
|
|
449
|
+
content.some(
|
|
450
|
+
(block) =>
|
|
451
|
+
typeof block === 'object' &&
|
|
452
|
+
block !== null &&
|
|
453
|
+
(block as { type?: unknown }).type === 'tool_result',
|
|
454
|
+
);
|
|
455
|
+
if (!carriesToolResult) out.user_prompt_submit += 1;
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
if (record.type === 'assistant') {
|
|
460
|
+
const content = (record.message as { content?: unknown } | undefined)?.content;
|
|
461
|
+
if (!Array.isArray(content)) return;
|
|
462
|
+
for (const block of content) {
|
|
463
|
+
if (typeof block !== 'object' || block === null) continue;
|
|
464
|
+
const typed = block as { type?: unknown; name?: unknown };
|
|
465
|
+
if (typed.type !== 'tool_use') continue;
|
|
466
|
+
out.tool_use_blocks += 1;
|
|
467
|
+
if (sidechain) out.sidechain_tool_use_blocks += 1;
|
|
468
|
+
if (typed.name === 'Agent' || typed.name === 'Task') {
|
|
469
|
+
out.agent_tool_use_blocks += 1;
|
|
470
|
+
if (sidechain) out.sidechain_agent_tool_use_blocks += 1;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/** Fold one transcript's counts into a denominator, honouring the window. */
|
|
477
|
+
export function accumulate(
|
|
478
|
+
into: HostDenominator,
|
|
479
|
+
counts: TranscriptCounts,
|
|
480
|
+
windowStart: string,
|
|
481
|
+
windowEnd: string,
|
|
482
|
+
): void {
|
|
483
|
+
into.transcripts_found += 1;
|
|
484
|
+
into.unparseable_lines += counts.unparseable_lines;
|
|
485
|
+
|
|
486
|
+
if (counts.first_at === null) {
|
|
487
|
+
into.sessions_undatable += 1;
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
const day = counts.first_at.slice(0, 10);
|
|
491
|
+
if (day < windowStart) {
|
|
492
|
+
into.sessions_before_window += 1;
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
if (day > windowEnd) {
|
|
496
|
+
// v1 filed this under `sessions_before_window`, which is reachable via
|
|
497
|
+
// `--days`, clock skew or a future-dated fixture and was silent because
|
|
498
|
+
// the totals still reconciled (R2 finding 10).
|
|
499
|
+
into.sessions_after_window += 1;
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
into.sessions_in_window += 1;
|
|
504
|
+
into.session_start += 1;
|
|
505
|
+
into.user_prompt_submit += counts.user_prompt_submit;
|
|
506
|
+
into.pre_tool_use += counts.tool_use_blocks;
|
|
507
|
+
into.post_tool_use += counts.tool_use_blocks;
|
|
508
|
+
into.subagent_start += counts.agent_tool_use_blocks;
|
|
509
|
+
into.subagent_stop += counts.agent_tool_use_blocks;
|
|
510
|
+
into.excluded_meta_user_records += counts.excluded_meta_user_records;
|
|
511
|
+
into.excluded_sidechain_user_records += counts.excluded_sidechain_user_records;
|
|
512
|
+
into.sidechain_tool_use_blocks += counts.sidechain_tool_use_blocks;
|
|
513
|
+
into.sidechain_agent_tool_use_blocks += counts.sidechain_agent_tool_use_blocks;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/** The total across the six counted cells — the denominator of the rate. */
|
|
517
|
+
export function totalCountedEvents(record: HostDenominator): number {
|
|
518
|
+
return COUNTED_EVENTS.reduce((sum, event) => sum + record[event], 0);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/** The total across the five journal-bound counted cells. */
|
|
522
|
+
export function totalJournalBoundEvents(record: HostDenominator): number {
|
|
523
|
+
return JOURNAL_BOUND_COUNTED_EVENTS.reduce((sum, event) => sum + record[event], 0);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* Claude Code's project-directory name for a working directory.
|
|
528
|
+
*
|
|
529
|
+
* The host replaces every path separator, and every `.`, with `-`. Derived by
|
|
530
|
+
* inspection of the directories this machine carries rather than from a
|
|
531
|
+
* documented rule, which is why {@link repositoryScopeSlugs} returns the slugs
|
|
532
|
+
* it computed: a slug matching no directory is then visible as an empty scope
|
|
533
|
+
* rather than as a silently small denominator.
|
|
534
|
+
*/
|
|
535
|
+
export function projectSlug(dir: string): string {
|
|
536
|
+
return dir.replace(/[/.]/g, '-');
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/** The result of asking git which directories belong to the measured repo. */
|
|
540
|
+
export interface RepositoryScope {
|
|
541
|
+
/** Absolute worktree paths, main checkout first. Empty outside a repo. */
|
|
542
|
+
worktrees: readonly string[];
|
|
543
|
+
/** The project-directory slugs those paths map to. */
|
|
544
|
+
slugs: readonly string[];
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Every worktree of the repository containing `root`, as project slugs.
|
|
549
|
+
*
|
|
550
|
+
* The numerator's journal lives at the repository's COMMON git dir and is
|
|
551
|
+
* therefore shared by every worktree of that repository and reachable from none
|
|
552
|
+
* other. So the matching denominator is exactly this set — which cannot be
|
|
553
|
+
* derived from a path prefix, because a linked worktree may live anywhere (this
|
|
554
|
+
* repository's own live under `/private/tmp/`).
|
|
555
|
+
*
|
|
556
|
+
* `git worktree list --porcelain` is the only source that knows. A failure
|
|
557
|
+
* returns an EMPTY scope rather than falling back to machine-wide: a silent
|
|
558
|
+
* widening is precisely the defect this function exists to fix.
|
|
559
|
+
*/
|
|
560
|
+
export function repositoryScopeSlugs(root: string): RepositoryScope {
|
|
561
|
+
let stdout: string;
|
|
562
|
+
try {
|
|
563
|
+
stdout = execFileSync('git', ['-C', root, 'worktree', 'list', '--porcelain'], {
|
|
564
|
+
encoding: 'utf8',
|
|
565
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
566
|
+
});
|
|
567
|
+
} catch {
|
|
568
|
+
return { worktrees: [], slugs: [] };
|
|
569
|
+
}
|
|
570
|
+
const worktrees: string[] = [];
|
|
571
|
+
for (const line of stdout.split('\n')) {
|
|
572
|
+
if (!line.startsWith('worktree ')) continue;
|
|
573
|
+
const dir = line.slice('worktree '.length).trim();
|
|
574
|
+
if (dir.length === 0) continue;
|
|
575
|
+
try {
|
|
576
|
+
worktrees.push(fs.realpathSync(dir));
|
|
577
|
+
} catch {
|
|
578
|
+
worktrees.push(dir);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
return { worktrees, slugs: worktrees.map(projectSlug) };
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/** What a scoped walk found, and what it could not read. */
|
|
585
|
+
export interface TranscriptWalk {
|
|
586
|
+
files: readonly string[];
|
|
587
|
+
/** Project directories skipped because they are outside the scope. */
|
|
588
|
+
outOfScope: number;
|
|
589
|
+
/** Directories that could not be listed. Reported, never swallowed. */
|
|
590
|
+
unreadable: number;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Every `*.jsonl` under a Claude Code projects root, recursively.
|
|
595
|
+
*
|
|
596
|
+
* Recursive on purpose: a flat one-level walk under-enumerates this machine's
|
|
597
|
+
* store by more than half (992 of 2,300 files), and a denominator that silently
|
|
598
|
+
* misses two thirds of its population is worse than none. Symlinked directories
|
|
599
|
+
* and transcripts are followed for the same reason — `isDirectory()` and
|
|
600
|
+
* `isFile()` are both false for a symlink, and v1 skipped them silently.
|
|
601
|
+
*
|
|
602
|
+
* `slugs` restricts the walk to the given top-level project directories. An
|
|
603
|
+
* EMPTY array means "no directory is in scope" and yields no files — never "all
|
|
604
|
+
* of them", which is the widening the scope field exists to prevent.
|
|
605
|
+
*/
|
|
606
|
+
export function findTranscripts(projectsRoot: string, slugs?: readonly string[]): TranscriptWalk {
|
|
607
|
+
const files: string[] = [];
|
|
608
|
+
let unreadable = 0;
|
|
609
|
+
let outOfScope = 0;
|
|
610
|
+
const inScope = slugs === undefined ? null : new Set(slugs);
|
|
611
|
+
|
|
612
|
+
const walk = (dir: string): void => {
|
|
613
|
+
let entries: fs.Dirent[];
|
|
614
|
+
try {
|
|
615
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
616
|
+
} catch {
|
|
617
|
+
unreadable += 1;
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
for (const entry of entries) {
|
|
621
|
+
const full = path.join(dir, entry.name);
|
|
622
|
+
let isDir = entry.isDirectory();
|
|
623
|
+
let isFile = entry.isFile();
|
|
624
|
+
if (entry.isSymbolicLink()) {
|
|
625
|
+
try {
|
|
626
|
+
const st = fs.statSync(full);
|
|
627
|
+
isDir = st.isDirectory();
|
|
628
|
+
isFile = st.isFile();
|
|
629
|
+
} catch {
|
|
630
|
+
unreadable += 1;
|
|
631
|
+
continue;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
if (isDir) walk(full);
|
|
635
|
+
else if (isFile && entry.name.endsWith('.jsonl')) files.push(full);
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
|
|
639
|
+
let top: fs.Dirent[];
|
|
640
|
+
try {
|
|
641
|
+
top = fs.readdirSync(projectsRoot, { withFileTypes: true });
|
|
642
|
+
} catch {
|
|
643
|
+
return { files: [], outOfScope: 0, unreadable: 1 };
|
|
644
|
+
}
|
|
645
|
+
for (const entry of top) {
|
|
646
|
+
const full = path.join(projectsRoot, entry.name);
|
|
647
|
+
if (entry.isFile() && entry.name.endsWith('.jsonl')) {
|
|
648
|
+
// A transcript directly under the root belongs to no project
|
|
649
|
+
// directory, so it can never be attributed to a repository scope.
|
|
650
|
+
if (inScope === null) files.push(full);
|
|
651
|
+
else outOfScope += 1;
|
|
652
|
+
continue;
|
|
653
|
+
}
|
|
654
|
+
if (inScope !== null && !inScope.has(entry.name)) {
|
|
655
|
+
outOfScope += 1;
|
|
656
|
+
continue;
|
|
657
|
+
}
|
|
658
|
+
walk(full);
|
|
659
|
+
}
|
|
660
|
+
files.sort();
|
|
661
|
+
return { files, outOfScope, unreadable };
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/** Stream one transcript file and count it. Unparseable lines are COUNTED. */
|
|
665
|
+
export async function countTranscriptFile(file: string): Promise<TranscriptCounts> {
|
|
666
|
+
const out = emptyTranscriptCounts();
|
|
667
|
+
const stream = readline.createInterface({
|
|
668
|
+
input: fs.createReadStream(file, { encoding: 'utf8' }),
|
|
669
|
+
crlfDelay: Infinity,
|
|
670
|
+
});
|
|
671
|
+
for await (const line of stream) {
|
|
672
|
+
if (line.trim().length === 0) continue;
|
|
673
|
+
try {
|
|
674
|
+
const parsed = JSON.parse(line);
|
|
675
|
+
if (typeof parsed === 'object' && parsed !== null) {
|
|
676
|
+
foldRecord(out, parsed as Record<string, unknown>);
|
|
677
|
+
} else {
|
|
678
|
+
out.unparseable_lines += 1;
|
|
679
|
+
}
|
|
680
|
+
} catch {
|
|
681
|
+
// A truncated tail line is not a host event — but it is not nothing
|
|
682
|
+
// either, and every other exclusion class here is published.
|
|
683
|
+
out.unparseable_lines += 1;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
return out;
|
|
687
|
+
}
|