@event4u/agent-config 15.0.0 → 16.1.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 +301 -347
- 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 +77 -29
- 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/contexts/authority/scope-growth-ownership.md +80 -0
- package/dist/agent-src/contexts/communication/rules-auto/user-interaction-mechanics.md +97 -0
- package/dist/agent-src/contexts/execution/auto-orchestration-activation.md +44 -3
- package/dist/agent-src/contexts/execution/autonomy-mechanics.md +15 -15
- 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/user-interaction.md +31 -30
- 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 +50 -0
- package/dist/agent-src/skills/error-handling-patterns/SKILL.md +9 -0
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +14 -4
- package/dist/agent-src/skills/experiment-loop/SKILL.md +31 -1
- 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/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 +188 -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/cli-delegate/check_update_banner.js +40 -2
- package/dist/cli-delegate/chunk-4E2W63DA.js +335 -0
- 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-2XY7ILBT.js → chunk-FC75PGJE.js} +42 -2
- package/dist/cli-delegate/{chunk-GYC3VRYN.js → chunk-FXYUYU4N.js} +57 -13
- package/dist/cli-delegate/{chunk-55ACLDUE.js → chunk-GJCBB2UG.js} +168 -10
- package/dist/cli-delegate/{chunk-AK6HODNQ.js → chunk-HDVVGPQW.js} +873 -539
- package/dist/cli-delegate/chunk-NUHLI2RX.js +80 -0
- package/dist/cli-delegate/{chunk-HZ52BRGG.js → chunk-P6ZHP5W2.js} +15 -90
- package/dist/cli-delegate/{chunk-TYSY33MD.js → chunk-QJWAUMJW.js} +12 -2
- 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-2KKXJW25.js → chunk-S3VFXMTM.js} +1 -1
- package/dist/cli-delegate/{chunk-3FKR64R5.js → chunk-UNCRJBT3.js} +31 -7
- 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/cmd_analyze_session.js +1 -1
- package/dist/cli-delegate/cmd_brand_status.js +1 -1
- package/dist/cli-delegate/cmd_conformance.js +918 -325
- package/dist/cli-delegate/cmd_doctor.js +7 -5
- package/dist/cli-delegate/cmd_explain.js +5 -3
- 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 +6 -6
- package/dist/cli-delegate/cmd_mcp_available.js +1 -1
- package/dist/cli-delegate/cmd_migrate.js +2 -2
- package/dist/cli-delegate/cmd_packs_active.js +3 -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 +2 -2
- 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 +14 -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 +87 -40
- 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 +2601 -1073
- 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 +2008 -1481
- 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 +1612 -667
- 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_lock.js +1 -1
- 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/io/yamlIO.js +50 -0
- package/dist/server/routes/install.js +8 -0
- package/dist/server/routes/wizard.js +9 -3
- package/dist/server/schemas/settings.js +17 -5
- package/dist/shared/missionExecution.js +97 -0
- package/dist/ui/assets/{index-D0jaCRPM.js → index-DH1xc62v.js} +17 -17
- package/dist/ui/index.html +1 -1
- package/docs/MIGRATION.md +103 -0
- package/docs/contracts/settings-classes.md +26 -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 +1 -1
- 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/config/agent-settings.template.yml +139 -27
- package/src/config/auto-rule-budget.json +16 -0
- package/src/config/ci-local-parity.yml +18 -5
- package/src/config/continuity-surface.json +28 -0
- package/src/config/gate-coverage.yml +146 -13
- package/src/config/gate-violation-baselines.json +6 -6
- package/src/config/hook-token-budget.json +2 -0
- package/src/config/host-injection-effect.json +1 -1
- package/src/config/loop-surfaces.yaml +215 -0
- package/src/config/platform-anchor.json +3 -2
- package/src/config/preamble-payload-budget.json +12 -7
- package/src/config/preamble-payload-exceptions.json +28 -0
- package/src/config/release-gate-locality.yml +11 -0
- package/src/config/routing-coverage-seed.json +18 -4
- 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 +4 -3
- 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 +90 -0
- package/src/scripts/_dispatch.bash +3 -1
- 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/base_ref_payload.ts +315 -0
- package/src/scripts/_lib/cascade_base.ts +94 -0
- package/src/scripts/_lib/changelog_eras.ts +7 -2
- 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/dropped_decision.ts +142 -0
- package/src/scripts/_lib/forge_protection.ts +267 -0
- package/src/scripts/_lib/hook_platform_keys.ts +27 -0
- package/src/scripts/_lib/installed_lock.ts +1 -1
- package/src/scripts/_lib/json_python_parity.ts +127 -0
- package/src/scripts/_lib/lean_projection_mode.ts +14 -3
- package/src/scripts/_lib/loop_surfaces.ts +382 -0
- package/src/scripts/_lib/measured_payload_ceiling.ts +424 -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 +402 -0
- package/src/scripts/_lib/orchestration_record.ts +11 -20
- package/src/scripts/_lib/payload_catalogue_completeness.ts +211 -0
- package/src/scripts/_lib/release_findings_ingest.ts +324 -0
- package/src/scripts/_lib/release_highlights.ts +199 -10
- package/src/scripts/_lib/release_holds.ts +493 -0
- package/src/scripts/_lib/release_migration_gate.ts +184 -0
- package/src/scripts/_lib/release_tag_dispatch.ts +63 -0
- package/src/scripts/_lib/review_baseline.ts +254 -0
- package/src/scripts/_lib/review_skipped_record.ts +61 -1
- package/src/scripts/_lib/rides_along.ts +102 -0
- package/src/scripts/_lib/settings_renamed_keys.ts +82 -0
- package/src/scripts/_lib/standing_bound_ratchet.ts +216 -103
- 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 +51 -4
- package/src/scripts/_lib/typed_op_grant.ts +246 -0
- package/src/scripts/_lib/typed_op_watch.ts +185 -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/ask_block_census.ts +285 -15
- package/src/scripts/check_beta_review_markers.ts +35 -4
- package/src/scripts/check_enforcement_matrix.ts +624 -0
- package/src/scripts/check_finding_dispositions.ts +167 -16
- package/src/scripts/check_gate_reachability.ts +173 -10
- package/src/scripts/check_generator_sync.ts +954 -0
- package/src/scripts/check_held_object_arrivals.ts +381 -0
- package/src/scripts/check_preamble_payload_budget.ts +474 -124
- package/src/scripts/check_release_highlights.ts +33 -1
- package/src/scripts/check_release_holds.ts +567 -0
- package/src/scripts/check_release_published.ts +1 -1
- package/src/scripts/check_reply_consistency.ts +86 -17
- package/src/scripts/check_rule_projection_integrity.ts +22 -12
- package/src/scripts/check_test_delta.ts +294 -0
- package/src/scripts/check_test_weakening.ts +299 -0
- package/src/scripts/closure_scan.ts +333 -0
- package/src/scripts/compile_hook_manifest.ts +43 -3
- package/src/scripts/council_record_shape.ts +209 -0
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +127 -12
- package/src/scripts/hooks/block_kernel_rule_writes.ts +12 -2
- package/src/scripts/hooks/concern_registry.ts +6 -0
- package/src/scripts/hooks/design_pass_hook.ts +128 -3
- package/src/scripts/hooks/end_review_nudge_hook.ts +101 -21
- package/src/scripts/hooks/evidence_independence.ts +96 -0
- package/src/scripts/hooks/memory_recall_hook.ts +540 -0
- package/src/scripts/hooks/obligation_settle_hook.ts +182 -0
- package/src/scripts/hooks/review_baseline_hook.ts +130 -0
- package/src/scripts/hooks/rule_inject_hook.ts +49 -1
- package/src/scripts/hooks/run_continuation_hook.ts +17 -20
- package/src/scripts/hooks/turn_end_gate_hook.ts +232 -14
- package/src/scripts/hooks_doctor.ts +89 -1
- package/src/scripts/hooks_status.ts +19 -0
- package/src/scripts/install.ts +107 -207
- package/src/scripts/interruption_report.ts +8 -2
- package/src/scripts/janitor.ts +15 -1
- package/src/scripts/lint_decision_classes.ts +464 -0
- package/src/scripts/lint_hook_manifest.ts +11 -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 +61 -0
- package/src/scripts/lint_roadmap_producers.ts +202 -0
- package/src/scripts/measure_rule_budget.ts +101 -5
- package/src/scripts/measure_turn_end_gate.ts +302 -5
- package/src/scripts/new_roadmap.ts +37 -0
- package/src/scripts/orchestration_record.ts +0 -5
- package/src/scripts/release.ts +66 -162
- package/src/scripts/release_drill.ts +460 -15
- package/src/scripts/release_env.ts +1 -1
- package/src/scripts/release_publication.ts +52 -1
- package/src/scripts/report_enforcement_drift.ts +566 -0
- package/src/scripts/report_held_object_arrivals.ts +456 -0
- package/src/scripts/report_host_injection_effect.ts +3 -11
- package/src/scripts/report_standing_payload_by_host.ts +26 -152
- package/src/scripts/schemas/agent-settings.schema.json +23 -0
- package/src/scripts/schemas/command.schema.json +4 -0
- package/src/scripts/scope_growth.ts +219 -0
- package/src/scripts/sync_agent_settings.ts +140 -18
- package/src/scripts/trigger_eval_grandfather.json +0 -1
- package/src/scripts/ui_conformance_probe.ts +622 -0
- package/src/server/io/yamlIO.ts +48 -0
- package/src/server/schemas/settings.ts +31 -7
- package/src/shared/missionExecution.ts +126 -0
- package/dist/cli-delegate/chunk-523HCFYO.js +0 -90
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The loop-surface inventory, and the reach question it exists to answer.
|
|
3
|
+
*
|
|
4
|
+
* Why an inventory at all.
|
|
5
|
+
*
|
|
6
|
+
* `archive/road-to-wired-instruments.md` closed four instances of one defect —
|
|
7
|
+
* an instrument this repository built, tested, and then connected to nothing —
|
|
8
|
+
* one instance at a time, and shipped five hand-written `grep` acceptance
|
|
9
|
+
* criteria instead of a detector. Two further instances were open the whole
|
|
10
|
+
* time and nothing in the tree could notice them. This module is the reader
|
|
11
|
+
* half of the fix: a declared list of loop surfaces and the exported helpers
|
|
12
|
+
* they are supposed to call, plus the one predicate that says whether a
|
|
13
|
+
* declared helper is called by anything at all.
|
|
14
|
+
*
|
|
15
|
+
* What it is NOT.
|
|
16
|
+
*
|
|
17
|
+
* Reader input. Nothing executes from it, no surface changes behaviour because
|
|
18
|
+
* of it, and there is no runner. That is Risk 3 of the roadmap that introduced
|
|
19
|
+
* it, stated as a constraint rather than a hope: the moment something
|
|
20
|
+
* dispatches off this file it has become the loop engine a recorded decision
|
|
21
|
+
* forbids.
|
|
22
|
+
*
|
|
23
|
+
* The reach predicate, and the correction that shaped it.
|
|
24
|
+
*
|
|
25
|
+
* The obvious predicate — "no file other than its own imports this export" —
|
|
26
|
+
* was measured against `loop_guards.ts` and calls nine of its fourteen exports
|
|
27
|
+
* dead. Most of those are wrong: a helper used by its own neighbours in the
|
|
28
|
+
* same file is reached every time that file is. So the criterion is
|
|
29
|
+
* occurrence-based and counts the declaration itself:
|
|
30
|
+
*
|
|
31
|
+
* an export is DEAD when it appears in `src/` exactly once,
|
|
32
|
+
* including its own declaration.
|
|
33
|
+
*
|
|
34
|
+
* Two is enough, deliberately. A second occurrence anywhere in `src/` means
|
|
35
|
+
* some production line names it; whether that line sits in the same file or
|
|
36
|
+
* another one is not a difference this predicate needs to resolve, and
|
|
37
|
+
* resolving it would mean writing a second import resolver.
|
|
38
|
+
*
|
|
39
|
+
* `tests/` is outside `src/` and therefore outside the count by construction —
|
|
40
|
+
* which is the point. A test-only caller is exactly the state both open
|
|
41
|
+
* instances are in.
|
|
42
|
+
*
|
|
43
|
+
* Why `cap` accepts a markdown anchor as well as a symbol.
|
|
44
|
+
*
|
|
45
|
+
* The roadmap step that specified this file asked for `file:symbol` and
|
|
46
|
+
* "never prose". Reproduced against the tree on 2026-09-13, that is
|
|
47
|
+
* unsatisfiable for two of the five surfaces and a category error for both:
|
|
48
|
+
* `verify-repair-loop` and `experiment-loop` are SKILLS, and a skill's bound
|
|
49
|
+
* is a documented knob, not a TypeScript constant. `grep -rn max_attempts
|
|
50
|
+
* src/` returns hits only inside the skill's own directory.
|
|
51
|
+
*
|
|
52
|
+
* The property that requirement was reaching for is preserved exactly:
|
|
53
|
+
* a bound must resolve to something that demonstrably EXISTS. So a `.ts`
|
|
54
|
+
* target must be a defined symbol, a `.md` target must be a literal token
|
|
55
|
+
* present in that file, and a markdown-bound row must additionally declare
|
|
56
|
+
* `bound_kind: prose` — the prose-ness is recorded rather than inferred, and
|
|
57
|
+
* `inventoryVerdict` reports those surfaces as a named set so a prose bound
|
|
58
|
+
* can never be read as equal to a code one.
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
import * as fs from 'node:fs';
|
|
62
|
+
import * as path from 'node:path';
|
|
63
|
+
|
|
64
|
+
import { parse as parseYaml } from 'yaml';
|
|
65
|
+
|
|
66
|
+
export const INVENTORY_REL = 'src/config/loop-surfaces.yaml';
|
|
67
|
+
|
|
68
|
+
/** Fields every surface row must carry. Order is the order they are reported in. */
|
|
69
|
+
export const SURFACE_FIELDS = [
|
|
70
|
+
'cap',
|
|
71
|
+
'no_progress',
|
|
72
|
+
'success_stop',
|
|
73
|
+
'checker',
|
|
74
|
+
'human_gate',
|
|
75
|
+
'terminal_vocabulary',
|
|
76
|
+
'production_consumers',
|
|
77
|
+
] as const;
|
|
78
|
+
|
|
79
|
+
export type SurfaceField = (typeof SURFACE_FIELDS)[number];
|
|
80
|
+
|
|
81
|
+
/** A loop surface — a thing that iterates under a bound. */
|
|
82
|
+
export interface LoopSurface {
|
|
83
|
+
id: string;
|
|
84
|
+
/** `code` or `prose` — which kind of artefact the surface itself is. */
|
|
85
|
+
kind: string;
|
|
86
|
+
/** `code` when `cap`/`no_progress` name symbols, `prose` when they name doc anchors. */
|
|
87
|
+
bound_kind: string;
|
|
88
|
+
cap: string;
|
|
89
|
+
no_progress: string;
|
|
90
|
+
success_stop: string;
|
|
91
|
+
checker: string;
|
|
92
|
+
human_gate: string;
|
|
93
|
+
terminal_vocabulary: string;
|
|
94
|
+
production_consumers: string[];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* An exported helper a surface is meant to call.
|
|
99
|
+
*
|
|
100
|
+
* Separated from surfaces because the reach question only applies here: a
|
|
101
|
+
* surface is reached by being bound (a hook slot, a skill invocation), an
|
|
102
|
+
* instrument is reached by being called, and only the second is answerable by
|
|
103
|
+
* counting occurrences of a symbol.
|
|
104
|
+
*/
|
|
105
|
+
export interface LoopInstrument {
|
|
106
|
+
id: string;
|
|
107
|
+
file: string;
|
|
108
|
+
role: string;
|
|
109
|
+
/** `experimental` exempts it from the reach requirement — with a deadline. */
|
|
110
|
+
status?: string | undefined;
|
|
111
|
+
/** ISO `YYYY-MM-DD`. Required under `status: experimental`; a past date reds. */
|
|
112
|
+
expires?: string | undefined;
|
|
113
|
+
note?: string | undefined;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export interface Inventory {
|
|
117
|
+
surfaces: LoopSurface[];
|
|
118
|
+
instruments: LoopInstrument[];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** `path/to/file.ts:SYMBOL` or `path/to/file.md:ANCHOR`. */
|
|
122
|
+
const BOUND_TARGET = /^([A-Za-z0-9_./-]+\.(?:ts|js|md)):([A-Za-z_$][A-Za-z0-9_$-]*)$/;
|
|
123
|
+
|
|
124
|
+
function str(row: Record<string, unknown>, key: string): string {
|
|
125
|
+
const v = row[key];
|
|
126
|
+
return v === undefined || v === null ? '' : String(v);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function readInventory(root: string): Inventory | null {
|
|
130
|
+
const p = path.join(root, INVENTORY_REL);
|
|
131
|
+
if (!fs.existsSync(p)) return null;
|
|
132
|
+
const doc = parseYaml(fs.readFileSync(p, 'utf-8')) as {
|
|
133
|
+
surfaces?: Record<string, Record<string, unknown>>;
|
|
134
|
+
instruments?: Record<string, Record<string, unknown>>;
|
|
135
|
+
} | null;
|
|
136
|
+
|
|
137
|
+
const surfaces: LoopSurface[] = [];
|
|
138
|
+
for (const [id, row] of Object.entries(doc?.surfaces ?? {})) {
|
|
139
|
+
surfaces.push({
|
|
140
|
+
id,
|
|
141
|
+
kind: str(row, 'kind'),
|
|
142
|
+
bound_kind: str(row, 'bound_kind'),
|
|
143
|
+
cap: str(row, 'cap'),
|
|
144
|
+
no_progress: str(row, 'no_progress'),
|
|
145
|
+
success_stop: str(row, 'success_stop'),
|
|
146
|
+
checker: str(row, 'checker'),
|
|
147
|
+
human_gate: str(row, 'human_gate'),
|
|
148
|
+
terminal_vocabulary: str(row, 'terminal_vocabulary'),
|
|
149
|
+
production_consumers: Array.isArray(row['production_consumers'])
|
|
150
|
+
? (row['production_consumers'] as unknown[]).map(String)
|
|
151
|
+
: [],
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const instruments: LoopInstrument[] = [];
|
|
156
|
+
for (const [id, row] of Object.entries(doc?.instruments ?? {})) {
|
|
157
|
+
instruments.push({
|
|
158
|
+
id,
|
|
159
|
+
file: str(row, 'file'),
|
|
160
|
+
role: str(row, 'role'),
|
|
161
|
+
status: row['status'] === undefined ? undefined : String(row['status']),
|
|
162
|
+
expires: row['expires'] === undefined ? undefined : String(row['expires']),
|
|
163
|
+
note: row['note'] === undefined ? undefined : String(row['note']),
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
surfaces.sort((a, b) => a.id.localeCompare(b.id));
|
|
168
|
+
instruments.sort((a, b) => a.id.localeCompare(b.id));
|
|
169
|
+
return { surfaces, instruments };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Every `.ts` / `.js` file under `src/`, sorted. Cached per root. */
|
|
173
|
+
const sourceCache = new Map<string, string[]>();
|
|
174
|
+
|
|
175
|
+
export function sourceFiles(root: string): string[] {
|
|
176
|
+
const hit = sourceCache.get(root);
|
|
177
|
+
if (hit !== undefined) return hit;
|
|
178
|
+
const out: string[] = [];
|
|
179
|
+
const walk = (dir: string): void => {
|
|
180
|
+
let entries: fs.Dirent[];
|
|
181
|
+
try {
|
|
182
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
183
|
+
} catch {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
for (const e of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
187
|
+
const full = path.join(dir, e.name);
|
|
188
|
+
if (e.isDirectory()) {
|
|
189
|
+
if (e.name === 'node_modules') continue;
|
|
190
|
+
walk(full);
|
|
191
|
+
} else if (e.name.endsWith('.ts') || e.name.endsWith('.js')) {
|
|
192
|
+
out.push(full);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
walk(path.join(root, 'src'));
|
|
197
|
+
sourceCache.set(root, out);
|
|
198
|
+
return out;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** Drop the file-list cache. The self-test builds throwaway trees. */
|
|
202
|
+
export function resetSourceCache(): void {
|
|
203
|
+
sourceCache.clear();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function escapeRe(s: string): string {
|
|
207
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* How many times `symbol` occurs anywhere under `src/`, declaration included.
|
|
212
|
+
*
|
|
213
|
+
* Word-bounded, so `stallSignal` does not match `stallSignalled`. Deliberately
|
|
214
|
+
* text-level: a symbol resolver would have to model re-exports, barrel files
|
|
215
|
+
* and type-only imports to beat this, and would then be a second
|
|
216
|
+
* implementation of the compiler. The loose version's failure mode is a count
|
|
217
|
+
* that is too HIGH — reporting a dead export as live, which is the quiet
|
|
218
|
+
* direction. It never manufactures an accusation.
|
|
219
|
+
*/
|
|
220
|
+
export function occurrences(root: string, symbol: string): number {
|
|
221
|
+
if (symbol === '') return 0;
|
|
222
|
+
const re = new RegExp(`\\b${escapeRe(symbol)}\\b`, 'g');
|
|
223
|
+
let n = 0;
|
|
224
|
+
for (const f of sourceFiles(root)) {
|
|
225
|
+
n += (fs.readFileSync(f, 'utf-8').match(re) ?? []).length;
|
|
226
|
+
}
|
|
227
|
+
return n;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** Does `file` DEFINE `symbol` (a `.ts` target) or merely contain it (a `.md` anchor)? */
|
|
231
|
+
export function resolvesTarget(root: string, file: string, token: string): boolean {
|
|
232
|
+
const p = path.join(root, file);
|
|
233
|
+
if (!fs.existsSync(p)) return false;
|
|
234
|
+
const text = fs.readFileSync(p, 'utf-8');
|
|
235
|
+
const esc = escapeRe(token);
|
|
236
|
+
if (file.endsWith('.md')) return new RegExp(`\\b${esc}\\b`).test(text);
|
|
237
|
+
const decl = new RegExp(`\\b(?:const|let|var|function|interface|type|class|enum)\\s+${esc}\\b`);
|
|
238
|
+
if (decl.test(text)) return true;
|
|
239
|
+
// Object keys, union members and class fields: `TOKEN:` / `TOKEN(` at line start.
|
|
240
|
+
return new RegExp(`^\\s*(?:readonly\\s+)?${esc}\\s*[:(]`, 'm').test(text);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export interface ReachFinding {
|
|
244
|
+
id: string;
|
|
245
|
+
file: string;
|
|
246
|
+
/** `dead` · `expired` · `no-expiry` · `bad-status` — why it reds. */
|
|
247
|
+
reason: string;
|
|
248
|
+
detail: string;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export interface ShapeFinding {
|
|
252
|
+
id: string;
|
|
253
|
+
reason: string;
|
|
254
|
+
detail: string;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export interface InventoryVerdict {
|
|
258
|
+
/** Surfaces with a missing field, or a bound that resolves to nothing. */
|
|
259
|
+
shape: ShapeFinding[];
|
|
260
|
+
/** Declared instruments with no production consumer and no valid exemption. */
|
|
261
|
+
reach: ReachFinding[];
|
|
262
|
+
/** Instruments reported live — the does-not-over-fire evidence. */
|
|
263
|
+
live: string[];
|
|
264
|
+
/** Instruments held by a valid, unexpired `experimental` exemption. */
|
|
265
|
+
exempt: string[];
|
|
266
|
+
/** Surfaces whose bound is documented prose, not a code symbol. Reported, never red. */
|
|
267
|
+
proseBound: string[];
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/** An instrument is reached when it occurs more than once in `src/`. */
|
|
271
|
+
export const REACH_MINIMUM_OCCURRENCES = 2;
|
|
272
|
+
|
|
273
|
+
const BOUND_FIELDS = ['cap', 'no_progress'] as const;
|
|
274
|
+
|
|
275
|
+
function checkSurface(root: string, s: LoopSurface, shape: ShapeFinding[]): void {
|
|
276
|
+
for (const f of SURFACE_FIELDS) {
|
|
277
|
+
const v = s[f];
|
|
278
|
+
const empty = Array.isArray(v) ? v.length === 0 : String(v).trim() === '';
|
|
279
|
+
if (empty) shape.push({ id: s.id, reason: 'missing-field', detail: `\`${f}\` is empty` });
|
|
280
|
+
}
|
|
281
|
+
if (s.bound_kind !== 'code' && s.bound_kind !== 'prose') {
|
|
282
|
+
shape.push({
|
|
283
|
+
id: s.id,
|
|
284
|
+
reason: 'bad-bound-kind',
|
|
285
|
+
detail: `\`bound_kind: ${s.bound_kind}\` — must be \`code\` or \`prose\``,
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
for (const f of BOUND_FIELDS) {
|
|
289
|
+
const raw = String(s[f]).trim();
|
|
290
|
+
if (raw === '') continue;
|
|
291
|
+
const m = BOUND_TARGET.exec(raw);
|
|
292
|
+
if (m === null) {
|
|
293
|
+
shape.push({
|
|
294
|
+
id: s.id,
|
|
295
|
+
reason: 'not-a-target',
|
|
296
|
+
detail: `\`${f}: ${raw}\` is not \`path/to/file.{ts,js,md}:TOKEN\``,
|
|
297
|
+
});
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
const [, file, token] = m as unknown as [string, string, string];
|
|
301
|
+
// A markdown bound must SAY it is prose. Undeclared, it would read as a
|
|
302
|
+
// code bound in every report that aggregates this file.
|
|
303
|
+
if (file.endsWith('.md') && s.bound_kind !== 'prose') {
|
|
304
|
+
shape.push({
|
|
305
|
+
id: s.id,
|
|
306
|
+
reason: 'undeclared-prose-bound',
|
|
307
|
+
detail: `\`${f}\` points into ${file} but the row is \`bound_kind: ${s.bound_kind}\``,
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
if (!resolvesTarget(root, file, token)) {
|
|
311
|
+
shape.push({
|
|
312
|
+
id: s.id,
|
|
313
|
+
reason: 'unresolved-target',
|
|
314
|
+
detail: `\`${f}\` names ${token} in ${file}, which does not carry it`,
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function checkInstrument(root: string, i: LoopInstrument, day: string, v: InventoryVerdict): void {
|
|
321
|
+
if (i.status !== undefined && i.status !== 'experimental') {
|
|
322
|
+
v.reach.push({
|
|
323
|
+
id: i.id,
|
|
324
|
+
file: i.file,
|
|
325
|
+
reason: 'bad-status',
|
|
326
|
+
detail: `\`status: ${i.status}\` is not a recognised exemption (only \`experimental\`)`,
|
|
327
|
+
});
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
if (i.status === 'experimental') {
|
|
331
|
+
const exp = (i.expires ?? '').trim();
|
|
332
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(exp)) {
|
|
333
|
+
v.reach.push({
|
|
334
|
+
id: i.id,
|
|
335
|
+
file: i.file,
|
|
336
|
+
reason: 'no-expiry',
|
|
337
|
+
detail: 'an `experimental` exemption needs `expires: YYYY-MM-DD`',
|
|
338
|
+
});
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
if (exp <= day) {
|
|
342
|
+
v.reach.push({
|
|
343
|
+
id: i.id,
|
|
344
|
+
file: i.file,
|
|
345
|
+
reason: 'expired',
|
|
346
|
+
detail: `the \`experimental\` exemption lapsed on ${exp} — decide, or move the date with a reason`,
|
|
347
|
+
});
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
v.exempt.push(i.id);
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
const n = occurrences(root, i.id);
|
|
354
|
+
if (n < REACH_MINIMUM_OCCURRENCES) {
|
|
355
|
+
v.reach.push({
|
|
356
|
+
id: i.id,
|
|
357
|
+
file: i.file,
|
|
358
|
+
reason: 'dead',
|
|
359
|
+
detail: `occurs ${String(n)}× in src/ (own declaration included) — nothing calls it`,
|
|
360
|
+
});
|
|
361
|
+
} else {
|
|
362
|
+
v.live.push(i.id);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
export function inventoryVerdict(root: string, today = new Date()): InventoryVerdict {
|
|
367
|
+
const v: InventoryVerdict = { shape: [], reach: [], live: [], exempt: [], proseBound: [] };
|
|
368
|
+
const inv = readInventory(root);
|
|
369
|
+
if (inv === null) return v;
|
|
370
|
+
|
|
371
|
+
for (const s of inv.surfaces) {
|
|
372
|
+
checkSurface(root, s, v.shape);
|
|
373
|
+
if (s.bound_kind === 'prose') v.proseBound.push(s.id);
|
|
374
|
+
}
|
|
375
|
+
const day = today.toISOString().slice(0, 10);
|
|
376
|
+
for (const i of inv.instruments) checkInstrument(root, i, day, v);
|
|
377
|
+
|
|
378
|
+
v.live.sort();
|
|
379
|
+
v.exempt.sort();
|
|
380
|
+
v.proseBound.sort();
|
|
381
|
+
return v;
|
|
382
|
+
}
|