@event4u/agent-config 9.14.0 → 9.16.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/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +118 -99
- package/README.md +8 -0
- package/dist/agent-src/commands/agents/audit.md +7 -7
- package/dist/agent-src/commands/agents/optimize.md +1 -1
- package/dist/agent-src/commands/analyze/reference-repo.md +1 -1
- package/dist/agent-src/commands/chat-history.md +1 -0
- package/dist/agent-src/commands/context.md +1 -0
- package/dist/agent-src/commands/council.md +1 -1
- package/dist/agent-src/commands/ghostwriter.md +1 -1
- package/dist/agent-src/commands/memory.md +2 -1
- package/dist/agent-src/commands/module.md +1 -0
- package/dist/agent-src/commands/optimize/agents-dir.md +1 -1
- package/dist/agent-src/commands/optimize/deep.md +5 -2
- package/dist/agent-src/commands/optimize/skills.md +5 -5
- package/dist/agent-src/commands/override.md +1 -0
- package/dist/agent-src/commands/prediction-pool.md +4 -4
- package/dist/agent-src/commands/research/deep.md +1 -1
- package/dist/agent-src/commands/roadmap.md +2 -1
- package/dist/agent-src/commands/rule-compliance-audit.md +1 -1
- package/dist/agent-src/commands/tdd.md +1 -0
- package/dist/agent-src/commands/tests.md +1 -0
- package/dist/agent-src/commands/upstream-contribute.md +5 -5
- package/dist/agent-src/commands/video/from-script.md +1 -1
- package/dist/agent-src/commands/video/from-song.md +1 -1
- package/dist/agent-src/commands/video/scene.md +1 -1
- package/dist/agent-src/commands/video/stitch.md +1 -1
- package/dist/agent-src/commands/video/storyboard.md +1 -1
- package/dist/agent-src/commands/worktree/create.md +2 -1
- package/dist/agent-src/contexts/augment-infrastructure.md +1 -1
- package/dist/agent-src/contexts/contracts/artifact-engagement-flow.md +5 -5
- package/dist/agent-src/contexts/contracts/command-suggestion-flow.md +3 -3
- package/dist/agent-src/contexts/contracts/emergency-triage-block.md +2 -2
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +4 -3
- package/dist/agent-src/contexts/execution/subagent-routing.md +19 -0
- package/dist/agent-src/contexts/override-system.md +1 -1
- package/dist/agent-src/ghostwriter/fictional-fixture-v1.md +1 -1
- package/dist/agent-src/rules/augment-edit-discipline.md +1 -1
- package/dist/agent-src/rules/autonomous-execution.md +2 -1
- package/dist/agent-src/rules/brand-consistency.md +1 -0
- package/dist/agent-src/rules/brand-source-of-truth.md +1 -0
- package/dist/agent-src/rules/code-comment-discipline.md +2 -0
- package/dist/agent-src/rules/communication-through-line.md +1 -0
- package/dist/agent-src/rules/content-quoting-floor.md +1 -0
- package/dist/agent-src/rules/cross-source-consistency.md +0 -1
- package/dist/agent-src/rules/downstream-changes.md +1 -0
- package/dist/agent-src/rules/external-code-graph-interop.md +1 -0
- package/dist/agent-src/rules/external-reference-deep-dive.md +1 -0
- package/dist/agent-src/rules/fast-path-marker-visibility.md +1 -0
- package/dist/agent-src/rules/finance-safety-floor.md +0 -1
- package/dist/agent-src/rules/icon-consistency.md +1 -0
- package/dist/agent-src/rules/image-likeness-and-rights.md +1 -0
- package/dist/agent-src/rules/improve-before-implement.md +1 -1
- package/dist/agent-src/rules/invite-challenge.md +1 -0
- package/dist/agent-src/rules/lethal-trifecta-guard.md +1 -0
- package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +1 -0
- package/dist/agent-src/rules/markdown-safe-codeblocks.md +1 -0
- package/dist/agent-src/rules/media-governance-routing.md +4 -3
- package/dist/agent-src/rules/media-sync-ground-truth.md +1 -0
- package/dist/agent-src/rules/no-attribution-footers.md +1 -0
- package/dist/agent-src/rules/no-decorative-emojis-in-git-surfaces.md +1 -0
- package/dist/agent-src/rules/no-pr-progress-comments.md +1 -0
- package/dist/agent-src/rules/onboarding-gate.md +1 -0
- package/dist/agent-src/rules/output-discipline.md +1 -0
- package/dist/agent-src/rules/prefer-enums-over-literals.md +1 -0
- package/dist/agent-src/rules/preservation-guard.md +1 -0
- package/dist/agent-src/rules/question-not-instruction.md +1 -0
- package/dist/agent-src/rules/roadmap-ci-steps-policy.md +1 -0
- package/dist/agent-src/rules/role-mode-adherence.md +1 -0
- package/dist/agent-src/rules/runtime-safety.md +1 -0
- package/dist/agent-src/rules/security-sensitive-stop.md +1 -0
- package/dist/agent-src/rules/session-canary.md +9 -2
- package/dist/agent-src/rules/source-confidentiality.md +1 -0
- package/dist/agent-src/rules/source-of-truth.md +1 -0
- package/dist/agent-src/rules/spreadsheet-source-quality.md +1 -0
- package/dist/agent-src/rules/think-before-action.md +1 -0
- package/dist/agent-src/rules/token-budget-discipline.md +1 -0
- package/dist/agent-src/rules/token-efficiency.md +1 -0
- package/dist/agent-src/rules/token-optimizer-maintenance.md +5 -5
- package/dist/agent-src/rules/tool-safety.md +1 -0
- package/dist/agent-src/rules/untrusted-input-defense.md +2 -0
- package/dist/agent-src/rules/user-interaction.md +1 -0
- package/dist/agent-src/rules/user-interrupt-priority.md +1 -0
- package/dist/agent-src/skills/adr-create/SKILL.md +1 -1
- package/dist/agent-src/skills/check-refs/SKILL.md +1 -1
- package/dist/agent-src/skills/competitive-positioning/SKILL.md +1 -1
- package/dist/agent-src/skills/condense-memory/SKILL.md +1 -1
- package/dist/agent-src/skills/context-authoring/SKILL.md +1 -1
- package/dist/agent-src/skills/copilot-agents-optimization/SKILL.md +1 -1
- package/dist/agent-src/skills/description-assist/SKILL.md +1 -1
- package/dist/agent-src/skills/github-ci/SKILL.md +1 -0
- package/dist/agent-src/skills/guideline-writing/SKILL.md +1 -1
- package/dist/agent-src/skills/lint-skills/SKILL.md +1 -1
- package/dist/agent-src/skills/readme-reviewer/SKILL.md +1 -1
- package/dist/agent-src/skills/roadmap-management/SKILL.md +8 -1
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +1 -1
- package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +1 -1
- package/dist/agent-src/skills/skill-writing/SKILL.md +4 -4
- package/dist/agent-src/skills/token-optimizer/SKILL.md +4 -4
- package/dist/agent-src/skills/upstream-contribute/SKILL.md +6 -6
- package/dist/agent-src/skills/using-git-worktrees/SKILL.md +25 -0
- package/dist/agent-src/templates/AGENTS.md +1 -1
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/command.md +1 -1
- package/dist/agent-src/templates/copilot-instructions.md +3 -3
- package/dist/agent-src/templates/rule.md +2 -2
- package/dist/agent-src/templates/scripts/work_engine/orchestration.ts +1 -1
- package/dist/agent-src/templates/skill-archive-note.md +5 -5
- package/dist/agent-src/templates/skill.md +1 -1
- package/dist/cli/agent-config.js +66 -19
- package/dist/cli/agent-config.js.map +1 -1
- package/dist/cli/main.js +9 -4
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/mcp/content.js +1 -1
- package/dist/cli/mcp/content.js.map +1 -1
- package/dist/cli/python/workspace_drive.js +2 -0
- package/dist/cli/python/workspace_drive.js.map +1 -1
- package/dist/cli/python/workspace_drive_health.js +2 -0
- package/dist/cli/python/workspace_drive_health.js.map +1 -1
- package/dist/cli/python/workspace_hosts.js +2 -0
- package/dist/cli/python/workspace_hosts.js.map +1 -1
- package/dist/cli/python/workspace_sessions.js +2 -0
- package/dist/cli/python/workspace_sessions.js.map +1 -1
- package/dist/cli/registry.js +1 -0
- package/dist/cli/registry.js.map +1 -1
- package/dist/cli-delegate/{chunk-A3HJ6NTI.js → chunk-63WSP6YZ.js} +2 -1
- package/dist/cli-delegate/{chunk-EJYIGZ3T.js → chunk-BKO2AZ7Z.js} +1 -1
- package/dist/cli-delegate/{chunk-GCVFLBP2.js → chunk-H574KOUH.js} +338 -494
- package/dist/cli-delegate/chunk-NXFWFAYV.js +339 -0
- package/dist/cli-delegate/{chunk-TEGJU2JX.js → chunk-ZXXJLL5K.js} +2 -2
- package/dist/cli-delegate/cmd_conformance.js +29 -23
- package/dist/cli-delegate/cmd_doctor.js +3 -3
- package/dist/cli-delegate/cmd_export.js +4 -4
- package/dist/cli-delegate/cmd_preflight.js +2 -2
- package/dist/cli-delegate/cmd_prune.js +4 -4
- package/dist/cli-delegate/cmd_settings_migrate.js +4 -4
- package/dist/cli-delegate/cmd_sync.js +4 -4
- package/dist/cli-delegate/cmd_uninstall.js +4 -4
- package/dist/cli-delegate/cmd_validate.js +4 -4
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +127 -95
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.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 +729 -430
- package/dist/install/install.mjs +52 -11
- package/dist/install/rule_scope.js +18 -2
- package/dist/install/rule_scope.js.map +1 -1
- package/dist/mcp/registry-manifest.json +2 -2
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +518 -411
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/claude_settings_hooks.js +30 -5
- package/dist/scripts/_lib/claude_settings_hooks.js.map +1 -1
- package/dist/scripts/_lib/scoped_projection.js +223 -0
- package/dist/scripts/_lib/scoped_projection.js.map +1 -0
- package/dist/scripts/_lib/surface_tiers.js +125 -0
- package/dist/scripts/_lib/surface_tiers.js.map +1 -0
- package/dist/scripts/_lib/token_count.js +166 -0
- package/dist/scripts/_lib/token_count.js.map +1 -0
- package/dist/scripts/_lib/trigger_eval_floors.js +8 -0
- package/dist/scripts/_lib/trigger_eval_floors.js.map +1 -1
- package/dist/scripts/condense.js +139 -9
- package/dist/scripts/condense.js.map +1 -1
- package/dist/scripts/project_thin_rules.js +450 -0
- package/dist/scripts/project_thin_rules.js.map +1 -0
- package/dist/server/routes/workspace.js +3 -1
- package/dist/server/routes/workspace.js.map +1 -1
- package/dist/server/schemas/settings.js +9 -3
- package/dist/server/schemas/settings.js.map +1 -1
- package/dist/shared/userMd/utils.js +1 -1
- package/dist/shared/userMd/utils.js.map +1 -1
- package/dist/ui/assets/index-DmR0mbk4.js +26 -0
- package/dist/ui/assets/index-DmR0mbk4.js.map +1 -0
- package/dist/ui/index.html +1 -1
- package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +1 -1
- package/package.json +14 -14
- package/src/agent-src/templates/AGENTS.md +1 -1
- package/src/agent-src/templates/scripts/work_engine/orchestration.ts +1 -1
- package/src/config/agent-settings.template.yml +61 -11
- package/src/config/budget-routing.json +7 -0
- package/src/config/ci-local-parity.yml +8 -0
- package/src/config/discovery/unassigned-artefacts.yml +23 -25
- package/src/config/evaluator-budgets.json +35 -5
- package/src/config/gate-coverage.yml +147 -0
- package/src/config/gate-violation-baselines.json +25 -0
- package/src/install/rule_scope.ts +27 -2
- package/src/scripts/_cli/cmd_conformance.ts +13 -24
- package/src/scripts/_dispatch.bash +10 -0
- package/src/scripts/_lib/claude_settings_hooks.ts +30 -4
- package/src/scripts/_lib/gate_baseline.ts +213 -0
- package/src/scripts/_lib/scoped_projection.ts +50 -0
- package/src/scripts/_lib/tier_budget_routing.ts +360 -0
- package/src/scripts/_lib/tool_adapter_registry.ts +8 -1
- package/src/scripts/_lib/trigger_eval_floors.ts +9 -0
- package/src/scripts/_lib/trigger_routers.ts +17 -5
- package/src/scripts/adr/regenerate_index.ts +4 -3
- package/src/scripts/ai-video/lib/parse-blueprint.sh +1 -1
- package/src/scripts/ai_council/compile_corpus.ts +1 -1
- package/src/scripts/anchor_scoring_run.ts +1 -1
- package/src/scripts/audit_cloud_compatibility.ts +37 -2
- package/src/scripts/audit_initial_context.ts +53 -19
- package/src/scripts/audit_likelihood.ts +31 -2
- package/src/scripts/audit_user_type_axis.ts +58 -11
- package/src/scripts/bench_hook_latency.ts +172 -41
- package/src/scripts/bench_persona_placebo.ts +1 -1
- package/src/scripts/bench_quality_run.ts +1 -1
- package/src/scripts/build_mcp_catalog.ts +12 -1
- package/src/scripts/check_always_budget.ts +122 -11
- package/src/scripts/check_augment_description_cap.ts +24 -3
- package/src/scripts/check_ci_strict_superset.ts +150 -0
- package/src/scripts/check_cli_registry_budget_sync.ts +168 -0
- package/src/scripts/check_cluster_patterns.ts +100 -0
- package/src/scripts/check_consumer_scope_flip.ts +26 -4
- package/src/scripts/check_evaluator_budgets.ts +198 -19
- package/src/scripts/check_gate_coverage.ts +338 -2
- package/src/scripts/check_generator_output_coverage.ts +2 -1
- package/src/scripts/check_iron_law_prominence.ts +1 -1
- package/src/scripts/check_kernel_rule_bundle.ts +1 -1
- package/src/scripts/check_no_new_legacy_path.ts +167 -3
- package/src/scripts/check_pack_size.ts +18 -4
- package/src/scripts/check_portability.ts +37 -7
- package/src/scripts/check_references.ts +19 -3
- package/src/scripts/check_release_pr_shape.ts +13 -0
- package/src/scripts/check_token_quality_golden.ts +1 -1
- package/src/scripts/check_token_regression.ts +9 -2
- package/src/scripts/condense.ts +155 -12
- package/src/scripts/cost/budget.mjs +77 -2
- package/src/scripts/evaluator_umbrella.sh +34 -1
- package/src/scripts/generate_capability_matrix.ts +8 -0
- package/src/scripts/generate_router_coverage_from_matrix.ts +155 -0
- package/src/scripts/hook_manifest.yaml +17 -3
- package/src/scripts/hooks/block_config_weakening.ts +337 -0
- package/src/scripts/hooks/concern_registry.ts +2 -0
- package/src/scripts/hooks/probe_throttle.ts +75 -0
- package/src/scripts/install-hooks.sh +24 -2
- package/src/scripts/install.ts +2 -2
- package/src/scripts/lint_agents_md.ts +9 -3
- package/src/scripts/lint_archived_skills.ts +1 -1
- package/src/scripts/lint_artefact_frontmatter.ts +185 -37
- package/src/scripts/lint_command_routing.ts +21 -52
- package/src/scripts/lint_command_verbs.ts +47 -28
- package/src/scripts/lint_context_spine_usage.ts +38 -12
- package/src/scripts/lint_framework_leakage.ts +25 -6
- package/src/scripts/lint_handoffs.ts +49 -2
- package/src/scripts/lint_media_policy_linkage.ts +62 -30
- package/src/scripts/lint_namespace.ts +92 -7
- package/src/scripts/lint_new_skill_gate.ts +39 -31
- package/src/scripts/lint_originality_shingles.ts +7 -1
- package/src/scripts/lint_override_kernel_guard.ts +21 -3
- package/src/scripts/lint_pack_boundaries.ts +461 -91
- package/src/scripts/lint_pack_dependencies.ts +24 -57
- package/src/scripts/lint_persona_governance.ts +1 -1
- package/src/scripts/lint_role_experiences.ts +34 -5
- package/src/scripts/lint_trigger_precision.ts +142 -0
- package/src/scripts/mcp_server/consumer_tool_catalog.json +12 -201
- package/src/scripts/mcp_server/tools.ts +1 -1
- package/src/scripts/prepack-check.mjs +40 -1
- package/src/scripts/prepack_router_targets.d.mts +7 -0
- package/src/scripts/prepack_router_targets.mjs +93 -0
- package/src/scripts/print_required_checks.ts +72 -18
- package/src/scripts/profile_staleness_hook.ts +12 -0
- package/src/scripts/record_evaluator_measurements.mjs +105 -0
- package/src/scripts/recruit_preflight.sh +1 -1
- package/src/scripts/release.ts +214 -7
- package/src/scripts/router_target_paths.d.mts +4 -0
- package/src/scripts/router_target_paths.mjs +69 -0
- package/src/scripts/router_telemetry.ts +52 -1
- package/src/scripts/routing_doctor.ts +560 -0
- package/src/scripts/rule_trigger_eval.ts +403 -0
- package/src/scripts/schemas/rule.schema.json +5 -1
- package/src/scripts/session_canary_hook.ts +96 -5
- package/src/scripts/skill_linter.ts +9 -28
- package/src/scripts/smoke/skills.sh +1 -1
- package/src/scripts/sweep_dead_scan_roots.ts +356 -10
- package/src/scripts/trigger_coverage.ts +13 -2
- package/src/scripts/update_counts.ts +1 -1
- package/src/scripts/validate_telegraph_carveouts.ts +1 -1
- package/src/scripts/wrapper_freshness_hook.ts +14 -0
- package/src/server/schemas/settings.ts +19 -5
- package/src/shared/userMd/utils.ts +1 -1
- package/dist/cli-delegate/chunk-TJIAM5FW.js +0 -133
- package/dist/ui/assets/index-BpSQt5vm.js +0 -40
- package/dist/ui/assets/index-BpSQt5vm.js.map +0 -1
package/src/scripts/install.ts
CHANGED
|
@@ -3434,7 +3434,7 @@ function _resolve_global_rule_scope(package_root: string): RuleScope {
|
|
|
3434
3434
|
// contradict, and this is the documented upgrade-compat path.
|
|
3435
3435
|
if (settings_path === null) {
|
|
3436
3436
|
try {
|
|
3437
|
-
return ruleScopeFromSettings(_load_default_settings(package_root));
|
|
3437
|
+
return ruleScopeFromSettings(_load_default_settings(package_root), package_root);
|
|
3438
3438
|
} catch {
|
|
3439
3439
|
return LEGACY_ALL;
|
|
3440
3440
|
}
|
|
@@ -3467,7 +3467,7 @@ function _resolve_global_rule_scope(package_root: string): RuleScope {
|
|
|
3467
3467
|
return LEGACY_ALL;
|
|
3468
3468
|
}
|
|
3469
3469
|
try {
|
|
3470
|
-
return ruleScopeFromSettings(parsed as Record<string, unknown
|
|
3470
|
+
return ruleScopeFromSettings(parsed as Record<string, unknown>, package_root);
|
|
3471
3471
|
} catch (e) {
|
|
3472
3472
|
warn(
|
|
3473
3473
|
`could not derive rule scope from ${settings_path} (${String(e)}) — ` +
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* historical quirks preserved (consumers pin the exact behaviour).
|
|
10
10
|
*
|
|
11
11
|
* Enforces caps + pointer-ratio + pointer-anatomy + emergency-triage
|
|
12
|
-
* contract from
|
|
12
|
+
* contract from `src/skills/agents-md-thin-root/SKILL.md`.
|
|
13
13
|
*
|
|
14
14
|
* Exit non-zero on any (a) FAIL, (b)–(e) error. WARN is informational.
|
|
15
15
|
*/
|
|
@@ -87,12 +87,18 @@ function _resolve(target_str: string, template: boolean): boolean {
|
|
|
87
87
|
return true;
|
|
88
88
|
}
|
|
89
89
|
const candidates = [path.join(ROOT, raw)];
|
|
90
|
+
// DEAD-ROOT REPAIR (road-to-renewal-foundation Phase 1): the
|
|
91
|
+
// retired-authoring-root fallbacks were unreachable (ADR-051 deleted that
|
|
92
|
+
// tree). They were harmless only while `dist/agent-src/` happened to
|
|
93
|
+
// be materialised; the moment it is not, a valid pointer is reported
|
|
94
|
+
// unresolved. `src/agent-src/` is the source of truth those paths project
|
|
95
|
+
// from.
|
|
90
96
|
if (template && raw.startsWith('.augment/')) {
|
|
91
|
-
candidates.push(path.join(ROOT, raw.replace('.augment/', '.agent-src.uncondensed/')));
|
|
92
97
|
candidates.push(path.join(ROOT, raw.replace('.augment/', 'dist/agent-src/')));
|
|
98
|
+
candidates.push(path.join(ROOT, raw.replace('.augment/', 'src/agent-src/')));
|
|
93
99
|
}
|
|
94
100
|
if (raw.startsWith('dist/agent-src/')) {
|
|
95
|
-
candidates.push(path.join(ROOT, raw.replace('dist/agent-src/', '
|
|
101
|
+
candidates.push(path.join(ROOT, raw.replace('dist/agent-src/', 'src/agent-src/')));
|
|
96
102
|
}
|
|
97
103
|
return candidates.some((c) => _exists(c));
|
|
98
104
|
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* frontmatter parser, same `artefact_roots()` skill-root discovery.
|
|
10
10
|
*
|
|
11
11
|
* Enforces the contract from
|
|
12
|
-
*
|
|
12
|
+
* src/agent-src/templates/skill-archive-note.md:
|
|
13
13
|
*
|
|
14
14
|
* 1. Every <slug>.md under agents/evidence/archived-skills/ has the six
|
|
15
15
|
* required frontmatter fields with valid values.
|
|
@@ -21,6 +21,9 @@ import * as path from 'node:path';
|
|
|
21
21
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
22
22
|
import { parse as parseYaml } from 'yaml';
|
|
23
23
|
|
|
24
|
+
import { SRC_AGENT, SRC_RULES, SRC_SKILLS, iter_commands } from './_lib/agent_src.js';
|
|
25
|
+
import { checkRatchet } from './_lib/gate_baseline.js';
|
|
26
|
+
import { DeadScopeError, assertScanned } from './_lib/scan_scope.js';
|
|
24
27
|
import { parse_frontmatter } from './validate_frontmatter.js';
|
|
25
28
|
|
|
26
29
|
const _HERE = fileURLToPath(import.meta.url);
|
|
@@ -31,6 +34,24 @@ let ROOT = path.resolve(path.dirname(_HERE), '..', '..');
|
|
|
31
34
|
let SRC = path.join(ROOT, '.agent-src.uncondensed');
|
|
32
35
|
let VOCAB_DIR = path.join(ROOT, 'src', 'config', 'discovery');
|
|
33
36
|
|
|
37
|
+
const _DEFAULT_ROOT = ROOT;
|
|
38
|
+
const _DEFAULT_SRC = SRC;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* True while a caller has repointed the roots at a fixture tree.
|
|
42
|
+
*
|
|
43
|
+
* Stateless on purpose (compared against the values captured at import, not a
|
|
44
|
+
* sticky flag) so that a test restoring the real paths in `afterEach` also
|
|
45
|
+
* restores real-repo behaviour. Two things key off it: the scan roots (an
|
|
46
|
+
* injected container is scanned EXCLUSIVELY — the shared resolver still points
|
|
47
|
+
* at the real repo and would otherwise drag the whole corpus into a fixture
|
|
48
|
+
* run) and the violation ratchet, which records repo debt and must never judge
|
|
49
|
+
* a fixture.
|
|
50
|
+
*/
|
|
51
|
+
function _paths_overridden(): boolean {
|
|
52
|
+
return ROOT !== _DEFAULT_ROOT || SRC !== _DEFAULT_SRC;
|
|
53
|
+
}
|
|
54
|
+
|
|
34
55
|
/** Test seam: override the module-level scan roots (mirrors monkeypatch). */
|
|
35
56
|
function _set_paths(opts: { root?: string; src?: string; vocabDir?: string }): void {
|
|
36
57
|
if (opts.root !== undefined) {
|
|
@@ -71,25 +92,84 @@ function _load_vocab(): [Set<string>, Set<string>, Set<string>] {
|
|
|
71
92
|
return [wsIds, packIds, quarantine];
|
|
72
93
|
}
|
|
73
94
|
|
|
95
|
+
/**
|
|
96
|
+
* The four scan roots, named individually rather than via one container.
|
|
97
|
+
*
|
|
98
|
+
* ADR-051 did not move the pre-ADR-051 source container somewhere else — it
|
|
99
|
+
* split it. `skills/` and `rules/` became top-level `src/` siblings, `templates/` went to
|
|
100
|
+
* `src/agent-src/templates`, and `commands/` stopped being a directory at all
|
|
101
|
+
* (each command is now `src/domains/<pack>/<subpath>/command.md`). Until
|
|
102
|
+
* 2026-08-02 `SRC` still named the emptied container, so every walk below hit
|
|
103
|
+
* a missing directory and the gate printed `0 artefact(s) clean` forever.
|
|
104
|
+
*
|
|
105
|
+
* Deliberately NOT the resolver's category-append view (`artefact_roots()`
|
|
106
|
+
* + `/templates`): that also yields `src/templates`, an unrelated
|
|
107
|
+
* consumer-scaffold tree this gate never scanned. Repairing a root must
|
|
108
|
+
* restore the old scope, not silently widen it.
|
|
109
|
+
*/
|
|
110
|
+
function _scan_roots(): Array<[root: string, pattern: string]> {
|
|
111
|
+
if (_paths_overridden()) {
|
|
112
|
+
// Fixture mode — the injected container is the whole world.
|
|
113
|
+
return [
|
|
114
|
+
[path.join(SRC, 'skills'), 'SKILL.md'],
|
|
115
|
+
[path.join(SRC, 'rules'), '*.md'],
|
|
116
|
+
[path.join(SRC, 'commands'), '*.md'],
|
|
117
|
+
[path.join(SRC, 'templates'), '*.md'],
|
|
118
|
+
];
|
|
119
|
+
}
|
|
120
|
+
return [
|
|
121
|
+
[SRC_SKILLS(), 'SKILL.md'],
|
|
122
|
+
[SRC_RULES(), '*.md'],
|
|
123
|
+
[path.join(SRC_AGENT(), 'templates'), '*.md'],
|
|
124
|
+
];
|
|
125
|
+
}
|
|
126
|
+
|
|
74
127
|
function _iter_artefacts(): string[] {
|
|
75
128
|
const out: string[] = [];
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
out.push(p);
|
|
81
|
-
}
|
|
82
|
-
for (const p of _rglobSorted(path.join(SRC, 'commands'), '*.md')) {
|
|
83
|
-
out.push(p);
|
|
84
|
-
}
|
|
85
|
-
if (_exists(path.join(SRC, 'templates'))) {
|
|
86
|
-
for (const p of _rglobSorted(path.join(SRC, 'templates'), '*.md')) {
|
|
129
|
+
const seen = new Set<string>();
|
|
130
|
+
const push = (p: string): void => {
|
|
131
|
+
if (!seen.has(p)) {
|
|
132
|
+
seen.add(p);
|
|
87
133
|
out.push(p);
|
|
88
134
|
}
|
|
135
|
+
};
|
|
136
|
+
for (const [root, pattern] of _scan_roots()) {
|
|
137
|
+
for (const p of _rglobSorted(root, pattern)) {
|
|
138
|
+
push(p);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (!_paths_overridden()) {
|
|
142
|
+
for (const p of iter_commands()) {
|
|
143
|
+
push(p);
|
|
144
|
+
}
|
|
89
145
|
}
|
|
90
146
|
return out;
|
|
91
147
|
}
|
|
92
148
|
|
|
149
|
+
/**
|
|
150
|
+
* Does this frontmatter actually declare an ADR-013 discovery block?
|
|
151
|
+
*
|
|
152
|
+
* Used only by the quarantine-collision check ("listed as unassigned AND
|
|
153
|
+
* declaring discovery metadata"). `workspaces`, `packs`, `lifecycle` and
|
|
154
|
+
* `install` are unambiguous ADR-013 names, so their mere presence counts.
|
|
155
|
+
*
|
|
156
|
+
* `trust` is NOT unambiguous: it is also a scalar field in two unrelated
|
|
157
|
+
* template schemas — `trust: durable` on a knowledge card and `trust: low` on a
|
|
158
|
+
* Class-C lesson card. A bare `'trust' in fm` test read those as discovery
|
|
159
|
+
* frontmatter and reported both quarantined templates as contradictions. They
|
|
160
|
+
* are not: ADR-013 `trust` is an OBJECT (`{level, confidence,
|
|
161
|
+
* human_review_required}`), a shape neither file has. So `trust` counts here
|
|
162
|
+
* only when it is object-shaped. Across the whole quarantine list these two
|
|
163
|
+
* strings were the only hits of any of the five names, so this is the complete
|
|
164
|
+
* fix, not a special case for two paths.
|
|
165
|
+
*/
|
|
166
|
+
function _declares_discovery_frontmatter(fm: Record<string, unknown>): boolean {
|
|
167
|
+
if (['workspaces', 'packs', 'lifecycle', 'install'].some((k) => k in fm)) {
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
return 'trust' in fm && isPlainObject(fm['trust']);
|
|
171
|
+
}
|
|
172
|
+
|
|
93
173
|
function _check_one(
|
|
94
174
|
p: string,
|
|
95
175
|
wsIds: Set<string>,
|
|
@@ -101,10 +181,7 @@ function _check_one(
|
|
|
101
181
|
if (quarantine.has(rel)) {
|
|
102
182
|
const text = fs.readFileSync(p, 'utf-8');
|
|
103
183
|
const [fm] = parse_frontmatter(text);
|
|
104
|
-
if (
|
|
105
|
-
isPlainObject(fm) &&
|
|
106
|
-
['workspaces', 'packs', 'lifecycle', 'trust', 'install'].some((k) => k in fm)
|
|
107
|
-
) {
|
|
184
|
+
if (isPlainObject(fm) && _declares_discovery_frontmatter(fm)) {
|
|
108
185
|
errs.push(
|
|
109
186
|
`${rel}: quarantined in unassigned-artefacts.yml but carries` +
|
|
110
187
|
' discovery frontmatter — remove one or the other.',
|
|
@@ -120,7 +197,22 @@ function _check_one(
|
|
|
120
197
|
return errs;
|
|
121
198
|
}
|
|
122
199
|
|
|
123
|
-
|
|
200
|
+
// Only the two keys that carry irreducible information are REQUIRED.
|
|
201
|
+
//
|
|
202
|
+
// ADR-013 Phase 4 read as "all five strictly enforced", and this gate was
|
|
203
|
+
// written that way — but it never ran (dead scan root), while
|
|
204
|
+
// `validate_frontmatter` DID run, green, for months against
|
|
205
|
+
// skill.schema.json, whose `required` list is
|
|
206
|
+
// ["name","description","source","domain"] and which gives `lifecycle`,
|
|
207
|
+
// `trust` and `install` documented defaults ("active"; every `trust` and
|
|
208
|
+
// `install` sub-property defaulted). Two gates asserted contradictory
|
|
209
|
+
// contracts and only one was ever enforced — the enforced one is the real
|
|
210
|
+
// contract. The corpus agrees and splits cleanly along exactly that line:
|
|
211
|
+
// `workspaces`/`packs` are absent 0 times in 618 artefacts, while
|
|
212
|
+
// lifecycle/trust/install are absent 574/459/456 times. A default cannot
|
|
213
|
+
// invent which workspace or pack an artefact belongs to; it can supply the
|
|
214
|
+
// other three. See the 2026-08-02 amendment in ADR-013.
|
|
215
|
+
for (const key of ['workspaces', 'packs']) {
|
|
124
216
|
if (!(key in fm)) {
|
|
125
217
|
errs.push(`${rel}: missing required key \`${key}\``);
|
|
126
218
|
}
|
|
@@ -156,42 +248,70 @@ function _check_one(
|
|
|
156
248
|
}
|
|
157
249
|
}
|
|
158
250
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
251
|
+
// lifecycle / trust / install: schema-defaulted, so ABSENCE is legal at
|
|
252
|
+
// BOTH levels — a missing key and a missing sub-key alike take the
|
|
253
|
+
// documented default. Anything actually written down is still fully
|
|
254
|
+
// validated; this narrows WHEN the checks fire, never what they accept.
|
|
255
|
+
//
|
|
256
|
+
// The sub-key half matters as much as the top-level half: an earlier pass
|
|
257
|
+
// defaulted an absent `trust` but still demanded all three sub-keys of a
|
|
258
|
+
// `trust:` block that was present, which is self-contradictory — the same
|
|
259
|
+
// default was honoured in one branch and refused in the other. It also had
|
|
260
|
+
// no basis in the corpus: across 288 skills, `trust` is complete 0 times,
|
|
261
|
+
// partial 120, absent 168. A required shape with 0/288 adoption was never
|
|
262
|
+
// the real requirement. `validate_frontmatter` — the gate that has actually
|
|
263
|
+
// been running — agrees: `apply_schema_defaults` fills missing sub-keys
|
|
264
|
+
// from the schema before validating, which is why it reports 0 failing on
|
|
265
|
+
// this same tree.
|
|
266
|
+
if ('lifecycle' in fm) {
|
|
267
|
+
const lc = fm['lifecycle'];
|
|
268
|
+
if (typeof lc !== 'string' || !LIFECYCLES.has(lc)) {
|
|
269
|
+
errs.push(`${rel}: lifecycle \`${lc}\` not in ${_sortedListRepr(LIFECYCLES)}`);
|
|
270
|
+
}
|
|
162
271
|
}
|
|
163
272
|
|
|
164
273
|
const trust = fm['trust'];
|
|
165
|
-
if (!
|
|
274
|
+
if (!('trust' in fm)) {
|
|
275
|
+
// schema default: {level: core, confidence: high, human_review_required: false}
|
|
276
|
+
} else if (!isPlainObject(trust)) {
|
|
166
277
|
errs.push(`${rel}: trust must be a mapping`);
|
|
167
278
|
} else {
|
|
168
|
-
const
|
|
169
|
-
if (
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
279
|
+
const t = trust as Record<string, unknown>;
|
|
280
|
+
if ('level' in t) {
|
|
281
|
+
const level = t['level'];
|
|
282
|
+
if (typeof level !== 'string' || !TRUST_LEVELS.has(level)) {
|
|
283
|
+
errs.push(
|
|
284
|
+
`${rel}: trust.level \`${_unwrap(level)}\` not in ${_sortedListRepr(TRUST_LEVELS)}`,
|
|
285
|
+
);
|
|
286
|
+
}
|
|
173
287
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
288
|
+
if ('confidence' in t) {
|
|
289
|
+
const confidence = t['confidence'];
|
|
290
|
+
if (typeof confidence !== 'string' || !TRUST_CONFIDENCE.has(confidence)) {
|
|
291
|
+
errs.push(
|
|
292
|
+
`${rel}: trust.confidence \`${_unwrap(confidence)}\` not in` +
|
|
293
|
+
` ${_sortedListRepr(TRUST_CONFIDENCE)}`,
|
|
294
|
+
);
|
|
295
|
+
}
|
|
180
296
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
297
|
+
if ('human_review_required' in t) {
|
|
298
|
+
if (typeof t['human_review_required'] !== 'boolean') {
|
|
299
|
+
errs.push(`${rel}: trust.human_review_required must be bool`);
|
|
300
|
+
}
|
|
184
301
|
}
|
|
185
302
|
}
|
|
186
303
|
|
|
187
304
|
const install = fm['install'];
|
|
188
|
-
if (!
|
|
305
|
+
if (!('install' in fm)) {
|
|
306
|
+
// schema default: {default: true, removable: false}
|
|
307
|
+
} else if (!isPlainObject(install)) {
|
|
189
308
|
errs.push(`${rel}: install must be a mapping`);
|
|
190
309
|
} else {
|
|
191
|
-
|
|
310
|
+
const i = install as Record<string, unknown>;
|
|
311
|
+
if ('default' in i && typeof i['default'] !== 'boolean') {
|
|
192
312
|
errs.push(`${rel}: install.default must be bool`);
|
|
193
313
|
}
|
|
194
|
-
if (
|
|
314
|
+
if ('removable' in i && typeof i['removable'] !== 'boolean') {
|
|
195
315
|
errs.push(`${rel}: install.removable must be bool`);
|
|
196
316
|
}
|
|
197
317
|
}
|
|
@@ -223,6 +343,22 @@ function main(argv?: readonly string[]): number {
|
|
|
223
343
|
|
|
224
344
|
const [wsIds, packIds, quarantine] = _load_vocab();
|
|
225
345
|
const artefacts = _iter_artefacts();
|
|
346
|
+
// Scope assertion: 0 artefacts means every root moved, not that the tree
|
|
347
|
+
// is clean. That was this gate's shipped state until 2026-08-02.
|
|
348
|
+
try {
|
|
349
|
+
assertScanned({
|
|
350
|
+
gate: 'lint_artefact_frontmatter',
|
|
351
|
+
scanned: artefacts.length,
|
|
352
|
+
units: 'artefact(s)',
|
|
353
|
+
roots: _scan_roots().map(([r]) => _relPosix(r) || '.'),
|
|
354
|
+
});
|
|
355
|
+
} catch (exc) {
|
|
356
|
+
if (!(exc instanceof DeadScopeError)) {
|
|
357
|
+
throw exc;
|
|
358
|
+
}
|
|
359
|
+
process.stderr.write(`❌ ${exc.message}\n`);
|
|
360
|
+
return 2;
|
|
361
|
+
}
|
|
226
362
|
const allErrs: string[] = [];
|
|
227
363
|
for (const p of artefacts) {
|
|
228
364
|
allErrs.push(..._check_one(p, wsIds, packIds, quarantine));
|
|
@@ -235,6 +371,18 @@ function main(argv?: readonly string[]): number {
|
|
|
235
371
|
process.stderr.write(
|
|
236
372
|
`\n${allErrs.length} violation(s) across ${artefacts.length} artefact(s).\n`,
|
|
237
373
|
);
|
|
374
|
+
if (!_paths_overridden()) {
|
|
375
|
+
const verdict = checkRatchet({
|
|
376
|
+
gate: 'lint_artefact_frontmatter',
|
|
377
|
+
actual: allErrs.length,
|
|
378
|
+
repoRoot: ROOT,
|
|
379
|
+
});
|
|
380
|
+
if (verdict.ok) {
|
|
381
|
+
process.stdout.write(`⚠️ ${verdict.message}\n`);
|
|
382
|
+
return 0;
|
|
383
|
+
}
|
|
384
|
+
process.stderr.write(`❌ ${verdict.message}\n`);
|
|
385
|
+
}
|
|
238
386
|
return 1;
|
|
239
387
|
}
|
|
240
388
|
if (!args.quiet) {
|
|
@@ -25,6 +25,8 @@ import * as path from 'node:path';
|
|
|
25
25
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
26
26
|
import { parse as parseYaml } from 'yaml';
|
|
27
27
|
|
|
28
|
+
import { DeadScopeError, assertScanned } from './_lib/scan_scope.js';
|
|
29
|
+
|
|
28
30
|
// src/scripts/lint_command_routing.ts → repo root is two dirs up. Mirrors the
|
|
29
31
|
// Python `Path(__file__).resolve().parent.parent.parent`.
|
|
30
32
|
const _HERE = fileURLToPath(import.meta.url);
|
|
@@ -109,61 +111,18 @@ function _rglobNameSorted(dir: string, name: string): string[] {
|
|
|
109
111
|
return out.sort();
|
|
110
112
|
}
|
|
111
113
|
|
|
112
|
-
/** Recursively list `*.md` files under `dir`, sorted. */
|
|
113
|
-
function _rglobMdSorted(dir: string): string[] {
|
|
114
|
-
const out: string[] = [];
|
|
115
|
-
const walk = (current: string): void => {
|
|
116
|
-
let entries: fs.Dirent[];
|
|
117
|
-
try {
|
|
118
|
-
entries = fs.readdirSync(current, { withFileTypes: true });
|
|
119
|
-
} catch {
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
for (const entry of entries) {
|
|
123
|
-
const full = path.join(current, entry.name);
|
|
124
|
-
if (entry.isDirectory()) {
|
|
125
|
-
walk(full);
|
|
126
|
-
} else if (entry.isFile() && entry.name.endsWith('.md')) {
|
|
127
|
-
out.push(full);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
};
|
|
131
|
-
walk(dir);
|
|
132
|
-
return out.sort();
|
|
133
|
-
}
|
|
134
|
-
|
|
135
114
|
/**
|
|
136
115
|
* Discover command sources in the post-ADR-051 layout.
|
|
137
116
|
*
|
|
138
|
-
* Authoring lives at src/domains/<domain>/**\/command.md
|
|
139
|
-
*
|
|
140
|
-
*
|
|
117
|
+
* Authoring lives at `src/domains/<domain>/**\/command.md`. A per-package
|
|
118
|
+
* `commands/` fallback used to sit behind this for "older checkouts"; that
|
|
119
|
+
* layout no longer exists in any supported checkout, so the branch was
|
|
120
|
+
* unreachable — and an unreachable fallback that takes over the moment its
|
|
121
|
+
* directory reappears is the trap this roadmap is closing, not a safety net.
|
|
141
122
|
*/
|
|
142
123
|
function _command_files(): string[] {
|
|
143
124
|
const domains = path.join(ROOT, 'src', 'domains');
|
|
144
|
-
|
|
145
|
-
return _rglobNameSorted(domains, 'command.md');
|
|
146
|
-
}
|
|
147
|
-
const packagesDir = path.join(ROOT, 'packages');
|
|
148
|
-
const legacyRoots: string[] = [];
|
|
149
|
-
let entries: fs.Dirent[];
|
|
150
|
-
try {
|
|
151
|
-
entries = fs.readdirSync(packagesDir, { withFileTypes: true });
|
|
152
|
-
} catch {
|
|
153
|
-
entries = [];
|
|
154
|
-
}
|
|
155
|
-
// glob("*/.agent-src.uncondensed/commands") — sorted, dirs only.
|
|
156
|
-
for (const entry of entries.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0))) {
|
|
157
|
-
const d = path.join(packagesDir, entry.name, '.agent-src.uncondensed', 'commands');
|
|
158
|
-
if (_isDir(d)) {
|
|
159
|
-
legacyRoots.push(d);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
const out: string[] = [];
|
|
163
|
-
for (const root of legacyRoots) {
|
|
164
|
-
out.push(..._rglobMdSorted(root));
|
|
165
|
-
}
|
|
166
|
-
return out;
|
|
125
|
+
return _isDir(domains) ? _rglobNameSorted(domains, 'command.md') : [];
|
|
167
126
|
}
|
|
168
127
|
|
|
169
128
|
// Central eval store in the post-ADR-051 layout. Eval stems use the
|
|
@@ -324,8 +283,18 @@ function main(): number {
|
|
|
324
283
|
const quiet = _quiet;
|
|
325
284
|
|
|
326
285
|
const files = _command_files();
|
|
327
|
-
|
|
328
|
-
|
|
286
|
+
try {
|
|
287
|
+
assertScanned({
|
|
288
|
+
gate: 'lint_command_routing',
|
|
289
|
+
scanned: files.length,
|
|
290
|
+
units: 'command file(s)',
|
|
291
|
+
roots: ['src/domains/**/command.md'],
|
|
292
|
+
});
|
|
293
|
+
} catch (exc) {
|
|
294
|
+
if (!(exc instanceof DeadScopeError)) {
|
|
295
|
+
throw exc;
|
|
296
|
+
}
|
|
297
|
+
process.stderr.write(`❌ ${exc.message}\n`);
|
|
329
298
|
return 3;
|
|
330
299
|
}
|
|
331
300
|
|
|
@@ -364,7 +333,7 @@ function main(): number {
|
|
|
364
333
|
if (!quiet) {
|
|
365
334
|
process.stdout.write(
|
|
366
335
|
`✅ ${visible} visible command(s) carry intent/routes_to/replaces ` +
|
|
367
|
-
|
|
336
|
+
`+ a routing eval (${files.length} command file(s) scanned).\n`,
|
|
368
337
|
);
|
|
369
338
|
}
|
|
370
339
|
return 0;
|
|
@@ -25,12 +25,24 @@ import * as path from 'node:path';
|
|
|
25
25
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
26
26
|
import { parse as parseYaml } from 'yaml';
|
|
27
27
|
|
|
28
|
+
import { iter_commands } from './_lib/agent_src.js';
|
|
29
|
+
import { DeadScopeError, assertScanned } from './_lib/scan_scope.js';
|
|
30
|
+
|
|
28
31
|
const _HERE = fileURLToPath(import.meta.url);
|
|
29
32
|
// src/scripts/lint_command_verbs.ts → three dirs up is the repo root.
|
|
30
33
|
// Mirrors Python `Path(__file__).resolve().parent.parent.parent`.
|
|
31
34
|
const ROOT = path.resolve(path.dirname(_HERE), '..', '..');
|
|
32
35
|
const VERBS_YML = path.join(ROOT, 'src', 'config', 'discovery', 'command-verbs.yml');
|
|
33
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Diff-path filter for "is this changed file a command?".
|
|
38
|
+
*
|
|
39
|
+
* ADR-051 replaced the flat `commands/` directory with one `command.md` leaf
|
|
40
|
+
* per verb under `src/domains/<pack>/<subpath>/`. Until 2026-08-02 this regex
|
|
41
|
+
* still matched the retired layout, so `changed_command_files()` matched
|
|
42
|
+
* nothing on any diff and the gate printed "No new/changed commands" no matter
|
|
43
|
+
* what landed — leaving the ADR-041 verb contract unenforced.
|
|
44
|
+
*/
|
|
45
|
+
const _CMD_PATH_RE = /^src\/domains\/.+\/command\.md$/;
|
|
34
46
|
const NAME_RE = /^name:\s*(.*)$/m;
|
|
35
47
|
const TIER_RE = /^tier:\s*(\d+)/m;
|
|
36
48
|
const VISIBILITY_RE = /^visibility:\s*['"]?([A-Za-z-]+)/m;
|
|
@@ -196,34 +208,21 @@ function _rglobMd(dir: string): string[] {
|
|
|
196
208
|
return out;
|
|
197
209
|
}
|
|
198
210
|
|
|
211
|
+
/**
|
|
212
|
+
* Every command in the tree, as `--all` sees it.
|
|
213
|
+
*
|
|
214
|
+
* Was an enumeration of per-package `commands/` directories, a layout ADR-051
|
|
215
|
+
* retired; it returned {} on every checkout after that. Now the shared
|
|
216
|
+
* resolver's command iterator, which knows the `src/domains` leaf shape.
|
|
217
|
+
*/
|
|
199
218
|
function all_visible_command_files(): Record<string, string> {
|
|
200
219
|
const out: Record<string, string> = {};
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
let pkgDirs: fs.Dirent[];
|
|
206
|
-
try {
|
|
207
|
-
pkgDirs = fs.readdirSync(pkgs, { withFileTypes: true });
|
|
208
|
-
} catch {
|
|
209
|
-
pkgDirs = [];
|
|
210
|
-
}
|
|
211
|
-
for (const d of pkgDirs) {
|
|
212
|
-
const cand = path.join(pkgs, d.name, '.agent-src.uncondensed', 'commands');
|
|
213
|
-
if (_exists(cand) && fs.statSync(cand).isDirectory()) {
|
|
214
|
-
roots.push(cand);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
roots = roots.sort();
|
|
218
|
-
}
|
|
219
|
-
for (const root of roots) {
|
|
220
|
-
for (const md of _rglobMd(root)) {
|
|
221
|
-
const parts = _relTo(md, ROOT).split('/');
|
|
222
|
-
if (path.basename(md) === 'AGENTS.md' || parts.includes('_archive')) {
|
|
223
|
-
continue;
|
|
224
|
-
}
|
|
225
|
-
out[_relTo(md, ROOT)] = 'A';
|
|
220
|
+
for (const md of iter_commands()) {
|
|
221
|
+
const rel = _relTo(md, ROOT);
|
|
222
|
+
if (path.basename(md) === 'AGENTS.md' || rel.split('/').includes('_archive')) {
|
|
223
|
+
continue;
|
|
226
224
|
}
|
|
225
|
+
out[rel] = 'A';
|
|
227
226
|
}
|
|
228
227
|
return out;
|
|
229
228
|
}
|
|
@@ -357,11 +356,31 @@ function main(): number {
|
|
|
357
356
|
const args = parse_args(process.argv.slice(2));
|
|
358
357
|
|
|
359
358
|
const { approved, banned, grandfathered } = load_config();
|
|
360
|
-
|
|
359
|
+
// Scope assertion: this is a diff gate, so an empty TARGET set is normal
|
|
360
|
+
// (nothing changed). What is never normal is an empty COMMAND CORPUS — that
|
|
361
|
+
// means the path filter and the walk root no longer describe the tree, and
|
|
362
|
+
// the diff can never match. Assert on the corpus, not on the diff.
|
|
363
|
+
const corpus = all_visible_command_files();
|
|
364
|
+
try {
|
|
365
|
+
assertScanned({
|
|
366
|
+
gate: 'lint_command_verbs',
|
|
367
|
+
scanned: Object.keys(corpus).length,
|
|
368
|
+
units: 'command file(s)',
|
|
369
|
+
roots: ['src/domains/**/command.md'],
|
|
370
|
+
});
|
|
371
|
+
} catch (exc) {
|
|
372
|
+
if (!(exc instanceof DeadScopeError)) {
|
|
373
|
+
throw exc;
|
|
374
|
+
}
|
|
375
|
+
process.stderr.write(`❌ ${exc.message}\n`);
|
|
376
|
+
return 2;
|
|
377
|
+
}
|
|
378
|
+
const targets = args.all ? corpus : changed_command_files(args.baseline);
|
|
361
379
|
if (Object.keys(targets).length === 0) {
|
|
362
380
|
if (!args.quiet) {
|
|
363
381
|
process.stdout.write(
|
|
364
|
-
`✅ No new/changed commands under
|
|
382
|
+
`✅ No new/changed commands under src/domains/ (baseline: ${args.baseline}; ` +
|
|
383
|
+
`${Object.keys(corpus).length} command(s) in scope).\n`,
|
|
365
384
|
);
|
|
366
385
|
}
|
|
367
386
|
return 0;
|
|
@@ -18,16 +18,24 @@ import * as fs from 'node:fs';
|
|
|
18
18
|
import * as path from 'node:path';
|
|
19
19
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
20
20
|
|
|
21
|
+
import { DeadScopeError, assertScanned } from './_lib/scan_scope.js';
|
|
22
|
+
|
|
21
23
|
const _HERE = fileURLToPath(import.meta.url);
|
|
22
24
|
const QUIET = process.argv.slice(2).includes('--quiet');
|
|
23
25
|
|
|
24
26
|
const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
/**
|
|
28
|
+
* Skill roots in precedence order — FIRST match wins; they are not unioned.
|
|
29
|
+
*
|
|
30
|
+
* The inherited list concatenated its patterns, which was safe only because
|
|
31
|
+
* the first one — the pre-ADR-051 source container — never matched anything
|
|
32
|
+
* after the move. So this gate has been linting the `dist/` projection rather
|
|
33
|
+
* than source. Repointing the first entry at `src/skills` while keeping the
|
|
34
|
+
* concatenation would double-count: both roots hold the same 288 skills.
|
|
35
|
+
* Hence first-wins — `src/skills` is the source of truth, `dist/` stays as the
|
|
36
|
+
* fallback for an installed tree that has no `src/`.
|
|
37
|
+
*/
|
|
38
|
+
const SKILL_GLOBS = ['src/skills', 'dist/agent-src/skills'] as const;
|
|
31
39
|
const VALID_SLOTS = [
|
|
32
40
|
'product',
|
|
33
41
|
'team',
|
|
@@ -136,13 +144,30 @@ function _relPosix(target: string, root: string): string {
|
|
|
136
144
|
}
|
|
137
145
|
|
|
138
146
|
function main(): number {
|
|
139
|
-
|
|
147
|
+
let skills: string[] = [];
|
|
148
|
+
let usedRoot = SKILL_GLOBS[0] as string;
|
|
140
149
|
for (const pattern of SKILL_GLOBS) {
|
|
141
|
-
|
|
150
|
+
const found = _rglobSkillSorted(path.join(REPO_ROOT, pattern));
|
|
151
|
+
if (found.length > 0) {
|
|
152
|
+
skills = found;
|
|
153
|
+
usedRoot = pattern;
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
142
156
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
157
|
+
// Scope assertion: no SKILL.md under ANY root means every root moved.
|
|
158
|
+
try {
|
|
159
|
+
assertScanned({
|
|
160
|
+
gate: 'lint_context_spine_usage',
|
|
161
|
+
scanned: skills.length,
|
|
162
|
+
units: 'SKILL.md file(s)',
|
|
163
|
+
roots: SKILL_GLOBS as readonly string[],
|
|
164
|
+
});
|
|
165
|
+
} catch (exc) {
|
|
166
|
+
if (!(exc instanceof DeadScopeError)) {
|
|
167
|
+
throw exc;
|
|
168
|
+
}
|
|
169
|
+
process.stderr.write(`❌ ${exc.message}\n`);
|
|
170
|
+
return 2;
|
|
146
171
|
}
|
|
147
172
|
let failed = 0;
|
|
148
173
|
let declared = 0;
|
|
@@ -172,7 +197,8 @@ function main(): number {
|
|
|
172
197
|
if (!QUIET) {
|
|
173
198
|
process.stdout.write(
|
|
174
199
|
`✅ ${declared} skill(s) declare context_spine; ` +
|
|
175
|
-
`all declared slots are cited in the body
|
|
200
|
+
`all declared slots are cited in the body ` +
|
|
201
|
+
`(${skills.length} SKILL.md scanned under ${usedRoot})\n`,
|
|
176
202
|
);
|
|
177
203
|
}
|
|
178
204
|
return 0;
|