@event4u/agent-config 1.19.0 → 1.21.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/.agent-src/commands/agent-handoff.md +14 -10
- package/.agent-src/commands/agents.md +1 -1
- package/.agent-src/commands/bug-fix.md +1 -1
- package/.agent-src/commands/bug-investigate.md +2 -2
- package/.agent-src/commands/chat-history/import.md +166 -0
- package/.agent-src/commands/chat-history/learn.md +178 -0
- package/.agent-src/commands/chat-history/show.md +17 -18
- package/.agent-src/commands/chat-history.md +26 -25
- package/.agent-src/commands/compress.md +12 -0
- package/.agent-src/commands/context/create.md +2 -2
- package/.agent-src/commands/context.md +1 -1
- package/.agent-src/commands/copilot-agents.md +1 -1
- package/.agent-src/commands/council/default.md +21 -12
- package/.agent-src/commands/council.md +1 -1
- package/.agent-src/commands/create-pr.md +28 -8
- package/.agent-src/commands/e2e-heal.md +1 -1
- package/.agent-src/commands/e2e-plan.md +1 -1
- package/.agent-src/commands/feature/dev.md +3 -3
- package/.agent-src/commands/feature.md +1 -1
- package/.agent-src/commands/fix/seeder.md +2 -2
- package/.agent-src/commands/fix.md +1 -1
- package/.agent-src/commands/jira-ticket.md +1 -1
- package/.agent-src/commands/judge.md +2 -2
- package/.agent-src/commands/memory.md +1 -1
- package/.agent-src/commands/mode.md +5 -5
- package/.agent-src/commands/module.md +1 -1
- package/.agent-src/commands/onboard.md +4 -4
- package/.agent-src/commands/optimize/augmentignore.md +1 -1
- package/.agent-src/commands/optimize-prompt.md +61 -0
- package/.agent-src/commands/optimize.md +1 -1
- package/.agent-src/commands/override.md +1 -1
- package/.agent-src/commands/review-changes.md +1 -1
- package/.agent-src/commands/review-routing.md +1 -1
- package/.agent-src/commands/roadmap.md +1 -1
- package/.agent-src/commands/set-cost-profile.md +3 -3
- package/.agent-src/commands/sync-agent-settings.md +2 -2
- package/.agent-src/commands/sync-gitignore.md +1 -1
- package/.agent-src/commands/tests/create.md +2 -2
- package/.agent-src/commands/tests.md +1 -1
- package/.agent-src/commands/threat-model.md +4 -4
- package/.agent-src/contexts/authority/commit-mechanics.md +14 -1
- package/.agent-src/contexts/authority/destructive-mechanics.md +14 -1
- package/.agent-src/contexts/authority/scope-mechanics.md +5 -0
- package/.agent-src/contexts/communication/rules-auto/guidelines-mechanics.md +76 -0
- package/.agent-src/contexts/communication/rules-auto/skill-quality-mechanics.md +76 -0
- package/.agent-src/contexts/communication/rules-auto/slash-command-routing-policy-mechanics.md +4 -4
- package/.agent-src/contexts/communication/rules-auto/think-before-action-mechanics.md +98 -0
- package/.agent-src/contexts/communication/rules-auto/token-efficiency-mechanics.md +93 -0
- package/.agent-src/contexts/communication/rules-auto/user-interaction-mechanics.md +125 -9
- package/.agent-src/contexts/execution/autonomy-mechanics.md +44 -0
- package/.agent-src/contexts/model-recommendations.md +2 -2
- package/.agent-src/contexts/override-system.md +1 -1
- package/.agent-src/personas/product-owner.md +2 -2
- package/.agent-src/personas/qa.md +1 -1
- package/.agent-src/rules/agent-authority.md +5 -6
- package/.agent-src/rules/agent-docs.md +11 -53
- package/.agent-src/rules/analysis-skill-routing.md +10 -40
- package/.agent-src/rules/architecture.md +6 -1
- package/.agent-src/rules/artifact-drafting-protocol.md +5 -0
- package/.agent-src/rules/artifact-engagement-recording.md +23 -59
- package/.agent-src/rules/ask-when-uncertain.md +24 -47
- package/.agent-src/rules/augment-portability.md +14 -62
- package/.agent-src/rules/augment-source-of-truth.md +10 -1
- package/.agent-src/rules/autonomous-execution.md +17 -98
- package/.agent-src/rules/capture-learnings.md +9 -80
- package/.agent-src/rules/cli-output-handling.md +12 -42
- package/.agent-src/rules/command-suggestion-policy.md +25 -73
- package/.agent-src/rules/commit-conventions.md +9 -58
- package/.agent-src/rules/commit-policy.md +16 -47
- package/.agent-src/rules/context-hygiene.md +5 -0
- package/.agent-src/rules/direct-answers.md +21 -42
- package/.agent-src/rules/docker-commands.md +11 -45
- package/.agent-src/rules/docs-sync.md +10 -56
- package/.agent-src/rules/downstream-changes.md +5 -0
- package/.agent-src/rules/e2e-testing.md +9 -44
- package/.agent-src/rules/guidelines.md +13 -75
- package/.agent-src/rules/improve-before-implement.md +10 -2
- package/.agent-src/rules/language-and-tone.md +35 -69
- package/.agent-src/rules/laravel-translations.md +11 -40
- package/.agent-src/rules/markdown-safe-codeblocks.md +4 -0
- package/.agent-src/rules/minimal-safe-diff.md +4 -0
- package/.agent-src/rules/missing-tool-handling.md +4 -0
- package/.agent-src/rules/model-recommendation.md +9 -61
- package/.agent-src/rules/no-attribution-footers.md +53 -0
- package/.agent-src/rules/no-cheap-questions.md +11 -27
- package/.agent-src/rules/no-council-references.md +76 -0
- package/.agent-src/rules/no-roadmap-references.md +8 -1
- package/.agent-src/rules/non-destructive-by-default.md +13 -43
- package/.agent-src/rules/onboarding-gate.md +9 -117
- package/.agent-src/rules/package-ci-checks.md +10 -37
- package/.agent-src/rules/php-coding.md +10 -55
- package/.agent-src/rules/preservation-guard.md +9 -0
- package/.agent-src/rules/review-routing-awareness.md +9 -97
- package/.agent-src/rules/reviewer-awareness.md +8 -83
- package/.agent-src/rules/roadmap-progress-sync.md +7 -170
- package/.agent-src/rules/role-mode-adherence.md +6 -2
- package/.agent-src/rules/rule-type-governance.md +8 -66
- package/.agent-src/rules/runtime-safety.md +5 -0
- package/.agent-src/rules/scope-control.md +17 -62
- package/.agent-src/rules/security-sensitive-stop.md +7 -1
- package/.agent-src/rules/size-enforcement.md +6 -1
- package/.agent-src/rules/skill-improvement-trigger.md +9 -49
- package/.agent-src/rules/skill-quality.md +7 -64
- package/.agent-src/rules/slash-command-routing-policy.md +11 -63
- package/.agent-src/rules/think-before-action.md +22 -87
- package/.agent-src/rules/token-efficiency.md +10 -74
- package/.agent-src/rules/token-optimizer-maintenance.md +68 -0
- package/.agent-src/rules/tool-safety.md +4 -0
- package/.agent-src/rules/ui-audit-gate.md +25 -61
- package/.agent-src/rules/upstream-proposal.md +9 -67
- package/.agent-src/rules/user-interaction.md +25 -95
- package/.agent-src/rules/verify-before-complete.md +1 -1
- package/.agent-src/skills/agent-docs-writing/SKILL.md +1 -1
- package/.agent-src/skills/ai-council/SKILL.md +69 -5
- package/.agent-src/skills/analysis-autonomous-mode/SKILL.md +1 -1
- package/.agent-src/skills/analysis-skill-router/SKILL.md +3 -3
- package/.agent-src/skills/artisan-commands/SKILL.md +2 -2
- package/.agent-src/skills/authz-review/SKILL.md +1 -1
- package/.agent-src/skills/aws-infrastructure/SKILL.md +5 -5
- package/.agent-src/skills/blast-radius-analyzer/SKILL.md +8 -8
- package/.agent-src/skills/bug-analyzer/SKILL.md +5 -5
- package/.agent-src/skills/code-refactoring/SKILL.md +4 -4
- package/.agent-src/skills/code-review/SKILL.md +2 -2
- package/.agent-src/skills/command-writing/SKILL.md +11 -0
- package/.agent-src/skills/composer-packages/SKILL.md +2 -2
- package/.agent-src/skills/context-authoring/SKILL.md +11 -0
- package/.agent-src/skills/context-document/SKILL.md +1 -1
- package/.agent-src/skills/copilot-agents-optimization/SKILL.md +23 -0
- package/.agent-src/skills/copilot-config/SKILL.md +1 -1
- package/.agent-src/skills/dcf-modeling/SKILL.md +89 -0
- package/.agent-src/skills/dependency-upgrade/SKILL.md +2 -2
- package/.agent-src/skills/devcontainer/SKILL.md +2 -2
- package/.agent-src/skills/developer-like-execution/SKILL.md +1 -1
- package/.agent-src/skills/docker/SKILL.md +1 -1
- package/.agent-src/skills/dto-creator/SKILL.md +1 -1
- package/.agent-src/skills/estimate-ticket/SKILL.md +2 -2
- package/.agent-src/skills/fe-design/SKILL.md +4 -4
- package/.agent-src/skills/feature-planning/SKILL.md +5 -5
- package/.agent-src/skills/funnel-analysis/SKILL.md +100 -0
- package/.agent-src/skills/laravel/SKILL.md +1 -1
- package/.agent-src/skills/laravel-notifications/SKILL.md +5 -5
- package/.agent-src/skills/laravel-pennant/SKILL.md +1 -1
- package/.agent-src/skills/laravel-pulse/SKILL.md +4 -4
- package/.agent-src/skills/laravel-reverb/SKILL.md +2 -2
- package/.agent-src/skills/laravel-scheduling/SKILL.md +1 -1
- package/.agent-src/skills/md-language-check/SKILL.md +1 -1
- package/.agent-src/skills/migration-creator/SKILL.md +7 -7
- package/.agent-src/skills/multi-tenancy/SKILL.md +8 -8
- package/.agent-src/skills/okr-tree-modeling/SKILL.md +93 -0
- package/.agent-src/skills/performance-analysis/SKILL.md +3 -3
- package/.agent-src/skills/pest-testing/SKILL.md +6 -6
- package/.agent-src/skills/php-service/SKILL.md +2 -2
- package/.agent-src/skills/project-analysis-hypothesis-driven/SKILL.md +3 -3
- package/.agent-src/skills/project-analysis-react/SKILL.md +1 -1
- package/.agent-src/skills/project-analysis-symfony/SKILL.md +1 -1
- package/.agent-src/skills/project-analysis-zend-laminas/SKILL.md +2 -2
- package/.agent-src/skills/project-analyzer/SKILL.md +4 -4
- package/.agent-src/skills/prompt-optimizer/SKILL.md +108 -0
- package/.agent-src/skills/readme-reviewer/SKILL.md +1 -1
- package/.agent-src/skills/rice-prioritization/SKILL.md +100 -0
- package/.agent-src/skills/rule-writing/SKILL.md +33 -0
- package/.agent-src/skills/sentry-integration/SKILL.md +1 -1
- package/.agent-src/skills/skill-writing/SKILL.md +14 -0
- package/.agent-src/skills/subagent-orchestration/SKILL.md +34 -2
- package/.agent-src/skills/terraform/SKILL.md +2 -2
- package/.agent-src/skills/terragrunt/SKILL.md +8 -8
- package/.agent-src/skills/test-performance/SKILL.md +5 -5
- package/.agent-src/skills/threat-modeling/SKILL.md +2 -2
- package/.agent-src/skills/token-optimizer/SKILL.md +110 -0
- package/.agent-src/skills/unit-economics-modeling/SKILL.md +104 -0
- package/.agent-src/skills/universal-project-analysis/SKILL.md +1 -1
- package/.agent-src/skills/using-git-worktrees/SKILL.md +1 -0
- package/.agent-src/templates/AGENTS.md +1 -1
- package/.agent-src/templates/agent-settings.md +25 -41
- package/.agent-src/templates/contexts/tenant-boundaries.md +2 -2
- package/.agent-src/templates/contexts.md +1 -1
- package/.agent-src/templates/copilot-instructions.md +21 -0
- package/.agent-src/templates/copilot-review-instructions.md +76 -0
- package/.agent-src/templates/features.md +1 -1
- package/.agent-src/templates/rule.md +127 -0
- package/.agent-src/templates/scripts/work_engine/hook_bootstrap.py +7 -5
- package/.agent-src/templates/scripts/work_engine/hooks/__init__.py +0 -4
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/__init__.py +0 -4
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/_chat_history_base.py +7 -51
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_append.py +1 -2
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_halt_append.py +1 -2
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/memory_visibility.py +2 -3
- package/.agent-src/templates/skill.md +30 -1
- package/.claude-plugin/marketplace.json +11 -4
- package/AGENTS.md +71 -3
- package/CHANGELOG.md +180 -3
- package/README.md +24 -23
- package/config/agent-settings.template.yml +63 -23
- package/config/gitignore-block.txt +11 -4
- package/docs/architecture.md +84 -3
- package/docs/catalog.md +23 -11
- package/docs/contracts/adr-chat-history-split.md +10 -1
- package/docs/contracts/agent-memory-contract.md +1 -1
- package/docs/contracts/command-clusters.md +1 -1
- package/docs/contracts/context-paths.md +2 -1
- package/docs/contracts/cross-wing-handoff.md +133 -0
- package/docs/contracts/file-ownership-matrix.json +678 -609
- package/docs/contracts/hook-architecture-v1.md +8 -1
- package/docs/contracts/iron-law-overrides.txt +25 -0
- package/docs/contracts/kernel-membership.md +273 -0
- package/docs/contracts/load-context-schema.md +26 -11
- package/docs/contracts/memory-visibility-v1.md +8 -24
- package/docs/contracts/pilot/agent-authority.md +24 -0
- package/docs/contracts/pilot/direct-answers.md +70 -0
- package/docs/contracts/pilot/language-and-tone.md +63 -0
- package/docs/contracts/rule-classification.md +170 -0
- package/docs/contracts/rule-router.md +153 -0
- package/docs/customization.md +18 -7
- package/docs/decisions/ADR-001-kernel-swap-deferred.md +109 -0
- package/docs/decisions/ADR-002-kernel-bucket-overrides.md +124 -0
- package/docs/decisions/ADR-rule-kernel-and-router.md +122 -0
- package/docs/getting-started.md +19 -27
- package/docs/guidelines/agent-infra/ask-when-uncertain-demos.md +1 -1
- package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +176 -0
- package/docs/guidelines/agent-infra/rule-type-governance.md +73 -0
- package/docs/guidelines/agent-infra/size-and-scope.md +13 -2
- package/docs/guidelines/agent-infra/skill-quality-checklist.md +119 -0
- package/docs/guidelines/augment-portability-patterns.md +68 -0
- package/docs/guidelines/php/php-coding-patterns.md +62 -0
- package/docs/hook-payload-capture.md +221 -0
- package/docs/migrations/commands-1.15.0.md +17 -12
- package/docs/skills-catalog.md +5 -4
- package/llms.txt +4 -3
- package/package.json +1 -1
- package/scripts/_p43_bodies.py +235 -0
- package/scripts/_p43_compress.py +118 -0
- package/scripts/_p4_migrate.py +199 -0
- package/scripts/_pilot_council_question.py +57 -0
- package/scripts/_pilot_measure.py +53 -0
- package/scripts/agent-config +1 -1
- package/scripts/ai_council/_default_prices.py +4 -4
- package/scripts/ai_council/clients.py +1 -1
- package/scripts/ai_council/modes.py +3 -4
- package/scripts/ai_council/pricing.py +10 -9
- package/scripts/ai_council/session.py +107 -5
- package/scripts/build_linear_digest.py +3 -5
- package/scripts/build_rule_trigger_matrix.py +1 -9
- package/scripts/chat_history.py +952 -596
- package/scripts/check_always_budget.py +39 -6
- package/scripts/check_compressed_paths.py +213 -0
- package/scripts/check_compression.py +15 -0
- package/scripts/check_context_paths.py +1 -0
- package/scripts/check_council_layout.py +105 -0
- package/scripts/check_council_references.py +145 -0
- package/scripts/check_portability.py +2 -0
- package/scripts/check_references.py +14 -2
- package/scripts/check_token_optimizer_freshness.py +131 -0
- package/scripts/compile_router.py +148 -0
- package/scripts/compress.py +219 -11
- package/scripts/council_cli.py +63 -9
- package/scripts/council_prune.py +81 -0
- package/scripts/count_token_optimizer_usage.sh +54 -0
- package/scripts/hook_manifest.yaml +33 -0
- package/scripts/hooks/augment-chat-history.sh +10 -0
- package/scripts/hooks/cowork-dispatcher.sh +98 -0
- package/scripts/hooks/dispatch_hook.py +35 -0
- package/scripts/hooks_status.py +12 -1
- package/scripts/install-hooks.sh +2 -2
- package/scripts/install.sh +81 -2
- package/scripts/iron_law_sha.py +98 -0
- package/scripts/lint_handoffs.py +214 -0
- package/scripts/lint_hook_manifest.py +2 -1
- package/scripts/lint_load_context.py +35 -5
- package/scripts/measure_rule_budget.py +314 -0
- package/scripts/prototype_lint_contradictions.py +150 -0
- package/scripts/redact_hook_capture.py +148 -0
- package/scripts/schemas/rule.schema.json +55 -6
- package/scripts/schemas/skill.schema.json +5 -0
- package/scripts/skill_linter.py +359 -7
- package/scripts/smoke_path_resolution.py +93 -0
- package/scripts/update_prices.py +3 -3
- package/scripts/validate_frontmatter.py +41 -1
- package/.agent-src/commands/chat-history/checkpoint.md +0 -126
- package/.agent-src/commands/chat-history/clear.md +0 -103
- package/.agent-src/commands/chat-history/resume.md +0 -183
- package/.agent-src/contexts/communication/rules-auto/artifact-engagement-recording-mechanics.md +0 -72
- package/.agent-src/contexts/communication/rules-auto/augment-portability-mechanics.md +0 -79
- package/.agent-src/contexts/communication/rules-auto/cli-output-handling-mechanics.md +0 -87
- package/.agent-src/contexts/communication/rules-auto/command-suggestion-policy-mechanics.md +0 -62
- package/.agent-src/contexts/communication/rules-auto/docs-sync-mechanics.md +0 -78
- package/.agent-src/contexts/communication/rules-auto/package-ci-checks-mechanics.md +0 -85
- package/.agent-src/contexts/communication/rules-auto/review-routing-awareness-mechanics.md +0 -65
- package/.agent-src/contexts/communication/rules-auto/roadmap-progress-sync-mechanics.md +0 -78
- package/.agent-src/contexts/communication/rules-auto/ui-audit-gate-mechanics.md +0 -53
- package/.agent-src/rules/chat-history-cadence.md +0 -143
- package/.agent-src/rules/chat-history-ownership.md +0 -124
- package/.agent-src/rules/chat-history-visibility.md +0 -97
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_heartbeat.py +0 -50
- package/.agent-src/templates/scripts/work_engine/hooks/builtin/chat_history_turn_check.py +0 -49
- package/scripts/check_phase_coupling.py +0 -148
- /package/{docs → .agent-src/contexts}/contracts/artifact-engagement-flow.md +0 -0
- /package/{docs → .agent-src/contexts}/contracts/command-suggestion-flow.md +0 -0
package/scripts/install.sh
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
# install.sh — Agent-config payload sync (one of two installer stages).
|
|
3
3
|
#
|
|
4
4
|
# Reads from vendor's .agent-src/ (fallback: .augment/ for pre-2.0 packages) and
|
|
5
|
-
# writes the target project's .augment/ tree: copies rules, symlinks everything
|
|
5
|
+
# writes the target project's .augment/ tree: copies rules, symlinks everything
|
|
6
|
+
# else. When augment.rules_use_symlinks: true is set in the target's
|
|
7
|
+
# .agent-settings.yml, rules are symlinked instead of copied.
|
|
6
8
|
# Creates tool-specific directories for Claude Code, Cursor, Cline, Windsurf, Gemini.
|
|
7
9
|
#
|
|
8
10
|
# Does NOT render .agent-settings.yml or bridge JSONs — that is the job of
|
|
@@ -35,6 +37,9 @@ DRY_RUN=false
|
|
|
35
37
|
VERBOSE=false
|
|
36
38
|
QUIET=false
|
|
37
39
|
SKIP_GITIGNORE=false
|
|
40
|
+
# Resolved from <TARGET>/.agent-settings.yml in resolve_settings(); when
|
|
41
|
+
# true, .augment/rules/ files are symlinked instead of copied.
|
|
42
|
+
USE_RULES_SYMLINKS=false
|
|
38
43
|
|
|
39
44
|
# --- Logging ---
|
|
40
45
|
log_info() { $QUIET || echo " ✅ $*"; }
|
|
@@ -114,6 +119,30 @@ EOF
|
|
|
114
119
|
|
|
115
120
|
# --- Utility functions ---
|
|
116
121
|
|
|
122
|
+
# Read augment.rules_use_symlinks from <TARGET>/.agent-settings.yml.
|
|
123
|
+
# Sets USE_RULES_SYMLINKS=true|false. Missing file or absent key → false.
|
|
124
|
+
# Minimal scoped parser; avoids a hard yq/python dependency.
|
|
125
|
+
resolve_settings() {
|
|
126
|
+
USE_RULES_SYMLINKS=false
|
|
127
|
+
local settings_file="$TARGET_DIR/.agent-settings.yml"
|
|
128
|
+
[[ -f "$settings_file" ]] || return 0
|
|
129
|
+
local val
|
|
130
|
+
val=$(awk '
|
|
131
|
+
/^[^[:space:]#]/ { in_block = ($0 ~ /^augment:[[:space:]]*$/) }
|
|
132
|
+
in_block && /^[[:space:]]+rules_use_symlinks[[:space:]]*:/ {
|
|
133
|
+
line = $0
|
|
134
|
+
sub(/^[[:space:]]*rules_use_symlinks[[:space:]]*:[[:space:]]*/, "", line)
|
|
135
|
+
sub(/[[:space:]]*#.*$/, "", line)
|
|
136
|
+
gsub(/[[:space:]]/, "", line)
|
|
137
|
+
print tolower(line)
|
|
138
|
+
exit
|
|
139
|
+
}
|
|
140
|
+
' "$settings_file" 2>/dev/null || true)
|
|
141
|
+
case "$val" in
|
|
142
|
+
true|yes|on|1) USE_RULES_SYMLINKS=true ;;
|
|
143
|
+
esac
|
|
144
|
+
}
|
|
145
|
+
|
|
117
146
|
# Check if a relative path should be copied (true=copy) or symlinked (false=symlink)
|
|
118
147
|
should_copy() {
|
|
119
148
|
local rel_path="$1"
|
|
@@ -127,6 +156,10 @@ should_copy() {
|
|
|
127
156
|
# Check against COPY_DIRS
|
|
128
157
|
for dir in $COPY_DIRS; do
|
|
129
158
|
if [[ "$first_segment" == "$dir" ]]; then
|
|
159
|
+
# Honor augment.rules_use_symlinks toggle for the rules dir.
|
|
160
|
+
if [[ "$dir" == "rules" ]] && $USE_RULES_SYMLINKS; then
|
|
161
|
+
return 1
|
|
162
|
+
fi
|
|
130
163
|
return 0
|
|
131
164
|
fi
|
|
132
165
|
done
|
|
@@ -563,6 +596,40 @@ copy_if_missing() {
|
|
|
563
596
|
cp "$source" "$target"
|
|
564
597
|
}
|
|
565
598
|
|
|
599
|
+
# Migrate legacy infra files from project root to agents/.
|
|
600
|
+
# Pre-2.x layout: .agent-chat-history (+ .bak), .agent-prices.md lived at
|
|
601
|
+
# the project root. They now live under agents/. Move them in place before
|
|
602
|
+
# any other content sync so the updated gitignore block (which lists
|
|
603
|
+
# /agents/.agent-chat-history*) and the chat-history hooks operate on the
|
|
604
|
+
# already-migrated layout. Idempotent: skips silently if the target already
|
|
605
|
+
# exists; never overwrites.
|
|
606
|
+
migrate_legacy_root_infra() {
|
|
607
|
+
local project_root="$1"
|
|
608
|
+
local agents_dir="$project_root/agents"
|
|
609
|
+
local items=(".agent-chat-history" ".agent-chat-history.bak" ".agent-prices.md")
|
|
610
|
+
|
|
611
|
+
for name in "${items[@]}"; do
|
|
612
|
+
local old="$project_root/$name"
|
|
613
|
+
local new="$agents_dir/$name"
|
|
614
|
+
|
|
615
|
+
[[ -e "$old" ]] || continue
|
|
616
|
+
|
|
617
|
+
if [[ -e "$new" ]]; then
|
|
618
|
+
log_warn "Legacy $name found at project root, but agents/$name already exists — leaving root copy in place"
|
|
619
|
+
continue
|
|
620
|
+
fi
|
|
621
|
+
|
|
622
|
+
if $DRY_RUN; then
|
|
623
|
+
log_verbose "would migrate $name → agents/$name"
|
|
624
|
+
continue
|
|
625
|
+
fi
|
|
626
|
+
|
|
627
|
+
mkdir -p "$agents_dir"
|
|
628
|
+
mv "$old" "$new"
|
|
629
|
+
log_info "Migrated $name → agents/$name"
|
|
630
|
+
done
|
|
631
|
+
}
|
|
632
|
+
|
|
566
633
|
# Ensure .gitignore contains the managed agent-config block.
|
|
567
634
|
# Delegates to scripts/sync_gitignore.py so the installer and the
|
|
568
635
|
# standalone /sync-gitignore command share one source of truth
|
|
@@ -632,9 +699,20 @@ main() {
|
|
|
632
699
|
$DRY_RUN && ! $QUIET && echo " Mode: DRY RUN"
|
|
633
700
|
echo ""
|
|
634
701
|
|
|
702
|
+
# 0. Migrate legacy infra files (root → agents/) before any content sync.
|
|
703
|
+
migrate_legacy_root_infra "$TARGET_DIR"
|
|
704
|
+
|
|
705
|
+
# 0b. Resolve settings (e.g. augment.rules_use_symlinks). On first
|
|
706
|
+
# install the file does not exist yet → defaults preserved.
|
|
707
|
+
resolve_settings
|
|
708
|
+
|
|
635
709
|
# 1. Hybrid sync payload → target/.augment/
|
|
636
710
|
sync_hybrid "$SOURCE_PAYLOAD" "$TARGET_DIR/.augment"
|
|
637
|
-
|
|
711
|
+
if $USE_RULES_SYMLINKS; then
|
|
712
|
+
log_info "Synced .augment/ (rules symlinked, rest symlinked)"
|
|
713
|
+
else
|
|
714
|
+
log_info "Synced .augment/ (rules copied, rest symlinked)"
|
|
715
|
+
fi
|
|
638
716
|
|
|
639
717
|
# 2. Copy standalone files from templates if missing on the target.
|
|
640
718
|
# We copy from templates/ (generic placeholders), NOT from the package's
|
|
@@ -643,6 +721,7 @@ main() {
|
|
|
643
721
|
# into consumer projects.
|
|
644
722
|
copy_if_missing "$SOURCE_PAYLOAD/templates/AGENTS.md" "$TARGET_DIR/AGENTS.md"
|
|
645
723
|
copy_if_missing "$SOURCE_PAYLOAD/templates/copilot-instructions.md" "$TARGET_DIR/.github/copilot-instructions.md"
|
|
724
|
+
copy_if_missing "$SOURCE_PAYLOAD/templates/copilot-review-instructions.md" "$TARGET_DIR/.github/copilot-review-instructions.md"
|
|
646
725
|
|
|
647
726
|
# 3. Create tool-specific symlinks
|
|
648
727
|
create_tool_symlinks "$TARGET_DIR"
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""SHA-256 of every triple-fence block in a rule file (Iron Law preservation).
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
python3 scripts/iron_law_sha.py <rule-id> [<rule-id> ...]
|
|
6
|
+
python3 scripts/iron_law_sha.py --all-kernel
|
|
7
|
+
python3 scripts/iron_law_sha.py --diff <rule-id> --against <baseline-sha>
|
|
8
|
+
|
|
9
|
+
The Iron-Law block is delimited by triple-backtick fences. Every line
|
|
10
|
+
inside any fence in the file is concatenated, whitespace-normalised
|
|
11
|
+
(runs of spaces collapsed; leading / trailing whitespace stripped per
|
|
12
|
+
line), case-folded, then SHA-256-hashed. Empty fences hash to
|
|
13
|
+
SHA-256(''), which is `e3b0c442…` (the well-known empty-string hash).
|
|
14
|
+
|
|
15
|
+
Acceptance per `road-to-kernel-and-router.md` P2.2: re-runnable,
|
|
16
|
+
deterministic, stdlib-only, no network. Compression of a kernel rule
|
|
17
|
+
must preserve this SHA (or surface a deliberate ADR-tracked diff).
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import argparse
|
|
23
|
+
import hashlib
|
|
24
|
+
import re
|
|
25
|
+
import sys
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
|
|
28
|
+
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
29
|
+
RULES_DIR = REPO_ROOT / ".agent-src.uncompressed" / "rules"
|
|
30
|
+
|
|
31
|
+
# Locked kernel set — kept in sync with measure_rule_budget.KERNEL_RULES.
|
|
32
|
+
KERNEL_RULES = (
|
|
33
|
+
"agent-authority",
|
|
34
|
+
"ask-when-uncertain",
|
|
35
|
+
"commit-policy",
|
|
36
|
+
"direct-answers",
|
|
37
|
+
"language-and-tone",
|
|
38
|
+
"no-cheap-questions",
|
|
39
|
+
"non-destructive-by-default",
|
|
40
|
+
"scope-control",
|
|
41
|
+
"verify-before-complete",
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
_FENCE_RE = re.compile(r"```(?:[^\n]*\n)([\s\S]*?)```")
|
|
45
|
+
_WS_RE = re.compile(r"\s+")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def iron_law_sha(text: str) -> str:
|
|
49
|
+
"""SHA-256 of all triple-fence content, whitespace-collapsed, upper-cased.
|
|
50
|
+
|
|
51
|
+
Algorithm matches `scripts/_pilot_measure.py` exactly so the SHAs
|
|
52
|
+
recorded in `docs/contracts/kernel-membership.md` § 2 stay
|
|
53
|
+
reproducible across pre / post compression.
|
|
54
|
+
"""
|
|
55
|
+
blocks = _FENCE_RE.findall(text)
|
|
56
|
+
norm = "".join(_WS_RE.sub(" ", b).strip().upper() for b in blocks)
|
|
57
|
+
return hashlib.sha256(norm.encode("utf-8")).hexdigest()
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def rule_sha(rule_id: str) -> str:
|
|
61
|
+
path = RULES_DIR / f"{rule_id}.md"
|
|
62
|
+
if not path.exists():
|
|
63
|
+
raise FileNotFoundError(path)
|
|
64
|
+
return iron_law_sha(path.read_text(encoding="utf-8"))
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def main(argv: list[str] | None = None) -> int:
|
|
68
|
+
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
|
69
|
+
parser.add_argument("rules", nargs="*", help="rule ids (omit if --all-kernel)")
|
|
70
|
+
parser.add_argument("--all-kernel", action="store_true", help="hash all 9 kernel rules")
|
|
71
|
+
parser.add_argument(
|
|
72
|
+
"--diff", metavar="RULE", help="hash one rule and compare to --against"
|
|
73
|
+
)
|
|
74
|
+
parser.add_argument("--against", metavar="SHA", help="expected SHA (for --diff)")
|
|
75
|
+
args = parser.parse_args(argv)
|
|
76
|
+
|
|
77
|
+
if args.diff:
|
|
78
|
+
if not args.against:
|
|
79
|
+
parser.error("--diff requires --against")
|
|
80
|
+
actual = rule_sha(args.diff)
|
|
81
|
+
match = actual == args.against
|
|
82
|
+
symbol = "✅" if match else "❌"
|
|
83
|
+
print(f"{symbol} {args.diff}: {actual} (expected {args.against})")
|
|
84
|
+
return 0 if match else 1
|
|
85
|
+
|
|
86
|
+
targets = list(KERNEL_RULES) if args.all_kernel else args.rules
|
|
87
|
+
if not targets:
|
|
88
|
+
parser.error("provide rule ids, or use --all-kernel")
|
|
89
|
+
|
|
90
|
+
width = max(len(t) for t in targets)
|
|
91
|
+
for rid in targets:
|
|
92
|
+
sha = rule_sha(rid)
|
|
93
|
+
print(f"{rid:<{width}} {sha}")
|
|
94
|
+
return 0
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
if __name__ == "__main__":
|
|
98
|
+
sys.exit(main())
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Lint cross-wing handoffs declared in senior-tier skills' ``## Related Skills`` blocks.
|
|
3
|
+
|
|
4
|
+
Builds a directed graph from every ``tier: senior`` skill's Related Skills
|
|
5
|
+
block (markdown links pointing at peer ``SKILL.md`` files), then enforces
|
|
6
|
+
the rules from ``docs/contracts/cross-wing-handoff.md`` § 4:
|
|
7
|
+
|
|
8
|
+
handoff_cycle — graph must be a DAG.
|
|
9
|
+
handoff_dangling — every linked target must exist.
|
|
10
|
+
handoff_tier_mismatch — senior may delegate only to senior.
|
|
11
|
+
|
|
12
|
+
Hooked into ``task lint-handoffs`` and ``task ci`` (between ``lint-skills``
|
|
13
|
+
and ``test``). Output mirrors ``scripts/skill_linter.py``: ``file:line:reason``.
|
|
14
|
+
|
|
15
|
+
Exit codes:
|
|
16
|
+
0 no violations
|
|
17
|
+
1 one or more violations
|
|
18
|
+
"""
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import re
|
|
22
|
+
import sys
|
|
23
|
+
from dataclasses import dataclass
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
from typing import Iterable
|
|
26
|
+
|
|
27
|
+
REPO = Path(__file__).resolve().parents[1]
|
|
28
|
+
SKILLS_DIR = REPO / ".agent-src.uncompressed" / "skills"
|
|
29
|
+
|
|
30
|
+
LINK_RE = re.compile(r"\[`?([a-z0-9][a-z0-9-]*)`?\]\(([^)]+SKILL\.md)\)")
|
|
31
|
+
RELATED_HEADING_RE = re.compile(r"^##\s+Related\s+Skills\s*$", re.IGNORECASE)
|
|
32
|
+
NEXT_HEADING_RE = re.compile(r"^##\s+\S")
|
|
33
|
+
WHEN_USE_RE = re.compile(r"^\*\*WHEN\s+to\s+use\s+this\*\*\s*$", re.IGNORECASE)
|
|
34
|
+
WHEN_NOT_RE = re.compile(r"^\*\*WHEN\s+NOT\s+to\s+use\s+this\*\*\s*$", re.IGNORECASE)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass(frozen=True)
|
|
38
|
+
class Violation:
|
|
39
|
+
file: Path
|
|
40
|
+
line: int
|
|
41
|
+
code: str
|
|
42
|
+
message: str
|
|
43
|
+
|
|
44
|
+
def render(self, repo: Path) -> str:
|
|
45
|
+
rel = self.file.relative_to(repo) if self.file.is_absolute() else self.file
|
|
46
|
+
return f"{rel}:{self.line}:{self.code}: {self.message}"
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def parse_frontmatter_tier(text: str) -> str | None:
|
|
50
|
+
if not text.startswith("---\n"):
|
|
51
|
+
return None
|
|
52
|
+
end = text.find("\n---\n", 4)
|
|
53
|
+
if end == -1:
|
|
54
|
+
return None
|
|
55
|
+
for raw in text[4:end].splitlines():
|
|
56
|
+
if ":" not in raw:
|
|
57
|
+
continue
|
|
58
|
+
key, _, val = raw.partition(":")
|
|
59
|
+
if key.strip() == "tier":
|
|
60
|
+
return val.strip().strip('"').strip("'")
|
|
61
|
+
return None
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def extract_related_block(text: str) -> tuple[int, list[tuple[int, str]]] | None:
|
|
65
|
+
"""Return (block_start_line, [(line, raw_line), ...]) for ``## Related Skills``."""
|
|
66
|
+
lines = text.splitlines()
|
|
67
|
+
start: int | None = None
|
|
68
|
+
for idx, line in enumerate(lines):
|
|
69
|
+
if RELATED_HEADING_RE.match(line):
|
|
70
|
+
start = idx
|
|
71
|
+
break
|
|
72
|
+
if start is None:
|
|
73
|
+
return None
|
|
74
|
+
body: list[tuple[int, str]] = []
|
|
75
|
+
for idx in range(start + 1, len(lines)):
|
|
76
|
+
if NEXT_HEADING_RE.match(lines[idx]):
|
|
77
|
+
break
|
|
78
|
+
body.append((idx + 1, lines[idx]))
|
|
79
|
+
return start + 1, body
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def split_when_subblocks(body: list[tuple[int, str]]) -> tuple[
|
|
83
|
+
list[tuple[int, str]], list[tuple[int, str]]
|
|
84
|
+
]:
|
|
85
|
+
"""Split a ``## Related Skills`` body into (when_to_use, when_not_to_use).
|
|
86
|
+
|
|
87
|
+
WHEN-to-use links are composition (delegation) edges — graph for cycles.
|
|
88
|
+
WHEN-NOT-to-use links are alternative pointers (peer cognition the user
|
|
89
|
+
picks instead) — never composition edges. Lines outside both sub-blocks
|
|
90
|
+
are treated as WHEN-to-use for backward compatibility.
|
|
91
|
+
"""
|
|
92
|
+
when_use: list[tuple[int, str]] = []
|
|
93
|
+
when_not: list[tuple[int, str]] = []
|
|
94
|
+
current = when_use
|
|
95
|
+
for lineno, raw in body:
|
|
96
|
+
if WHEN_USE_RE.match(raw):
|
|
97
|
+
current = when_use
|
|
98
|
+
continue
|
|
99
|
+
if WHEN_NOT_RE.match(raw):
|
|
100
|
+
current = when_not
|
|
101
|
+
continue
|
|
102
|
+
current.append((lineno, raw))
|
|
103
|
+
return when_use, when_not
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def extract_links(body: list[tuple[int, str]]) -> list[tuple[int, str, str]]:
|
|
107
|
+
"""Yield ``(line, slug, target_path)`` for every markdown link in the block."""
|
|
108
|
+
out: list[tuple[int, str, str]] = []
|
|
109
|
+
for lineno, raw in body:
|
|
110
|
+
for match in LINK_RE.finditer(raw):
|
|
111
|
+
out.append((lineno, match.group(1), match.group(2)))
|
|
112
|
+
return out
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def resolve_target(skill_file: Path, link: str) -> Path:
|
|
116
|
+
return (skill_file.parent / link).resolve()
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def detect_cycles(graph: dict[Path, set[Path]]) -> list[list[Path]]:
|
|
120
|
+
cycles: list[list[Path]] = []
|
|
121
|
+
visited: set[Path] = set()
|
|
122
|
+
stack: list[Path] = []
|
|
123
|
+
on_stack: set[Path] = set()
|
|
124
|
+
|
|
125
|
+
def dfs(node: Path) -> None:
|
|
126
|
+
if node in on_stack:
|
|
127
|
+
i = stack.index(node)
|
|
128
|
+
cycles.append(stack[i:] + [node])
|
|
129
|
+
return
|
|
130
|
+
if node in visited:
|
|
131
|
+
return
|
|
132
|
+
visited.add(node)
|
|
133
|
+
on_stack.add(node)
|
|
134
|
+
stack.append(node)
|
|
135
|
+
for nxt in graph.get(node, ()):
|
|
136
|
+
dfs(nxt)
|
|
137
|
+
stack.pop()
|
|
138
|
+
on_stack.discard(node)
|
|
139
|
+
|
|
140
|
+
for node in list(graph):
|
|
141
|
+
dfs(node)
|
|
142
|
+
return cycles
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def lint(skills_dir: Path) -> list[Violation]:
|
|
146
|
+
senior_skills: dict[Path, str] = {}
|
|
147
|
+
all_skills: dict[Path, str] = {}
|
|
148
|
+
for skill_md in sorted(skills_dir.rglob("SKILL.md")):
|
|
149
|
+
text = skill_md.read_text(encoding="utf-8")
|
|
150
|
+
tier = parse_frontmatter_tier(text)
|
|
151
|
+
all_skills[skill_md.resolve()] = tier or ""
|
|
152
|
+
if tier == "senior":
|
|
153
|
+
senior_skills[skill_md.resolve()] = text
|
|
154
|
+
|
|
155
|
+
violations: list[Violation] = []
|
|
156
|
+
graph: dict[Path, set[Path]] = {}
|
|
157
|
+
|
|
158
|
+
for skill_path, text in senior_skills.items():
|
|
159
|
+
block = extract_related_block(text)
|
|
160
|
+
if block is None:
|
|
161
|
+
continue
|
|
162
|
+
_, body = block
|
|
163
|
+
when_use, when_not = split_when_subblocks(body)
|
|
164
|
+
|
|
165
|
+
# WHEN-to-use links: composition edges (graph) + dangling/tier checks.
|
|
166
|
+
for lineno, slug, link in extract_links(when_use):
|
|
167
|
+
target = resolve_target(skill_path, link)
|
|
168
|
+
graph.setdefault(skill_path, set()).add(target)
|
|
169
|
+
if target not in all_skills:
|
|
170
|
+
violations.append(Violation(skill_path, lineno, "handoff_dangling",
|
|
171
|
+
f"link to `{slug}` resolves to missing file {link}"))
|
|
172
|
+
continue
|
|
173
|
+
if all_skills[target] != "senior":
|
|
174
|
+
violations.append(Violation(skill_path, lineno, "handoff_tier_mismatch",
|
|
175
|
+
f"senior skill links to non-senior `{slug}` "
|
|
176
|
+
f"(tier={all_skills[target] or 'unset'!r})"))
|
|
177
|
+
|
|
178
|
+
# WHEN-NOT-to-use links: alternative pointers, NOT composition edges.
|
|
179
|
+
# Dangling + tier-mismatch still apply (a broken alternative is wrong);
|
|
180
|
+
# cycles do not (mutual "use X instead" pointers are intentional).
|
|
181
|
+
for lineno, slug, link in extract_links(when_not):
|
|
182
|
+
target = resolve_target(skill_path, link)
|
|
183
|
+
if target not in all_skills:
|
|
184
|
+
violations.append(Violation(skill_path, lineno, "handoff_dangling",
|
|
185
|
+
f"link to `{slug}` resolves to missing file {link}"))
|
|
186
|
+
continue
|
|
187
|
+
if all_skills[target] != "senior":
|
|
188
|
+
violations.append(Violation(skill_path, lineno, "handoff_tier_mismatch",
|
|
189
|
+
f"senior skill links to non-senior `{slug}` "
|
|
190
|
+
f"(tier={all_skills[target] or 'unset'!r})"))
|
|
191
|
+
|
|
192
|
+
for cycle in detect_cycles(graph):
|
|
193
|
+
names = " → ".join(p.parent.name for p in cycle)
|
|
194
|
+
violations.append(Violation(cycle[0], 1, "handoff_cycle",
|
|
195
|
+
f"composition cycle: {names}"))
|
|
196
|
+
return violations
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def main(argv: list[str] | None = None) -> int:
|
|
200
|
+
skills_dir = SKILLS_DIR
|
|
201
|
+
if argv:
|
|
202
|
+
skills_dir = Path(argv[0]).resolve()
|
|
203
|
+
violations = lint(skills_dir)
|
|
204
|
+
if not violations:
|
|
205
|
+
print(f"✅ lint_handoffs: no violations under {skills_dir.relative_to(REPO)}")
|
|
206
|
+
return 0
|
|
207
|
+
for v in violations:
|
|
208
|
+
print(v.render(REPO))
|
|
209
|
+
print(f"\n❌ lint_handoffs: {len(violations)} violation(s)", file=sys.stderr)
|
|
210
|
+
return 1
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
if __name__ == "__main__":
|
|
214
|
+
sys.exit(main(sys.argv[1:]))
|
|
@@ -57,7 +57,8 @@ EVENT_VOCABULARY: set[str] = {
|
|
|
57
57
|
# Known platform identifiers. New platforms MUST be added here as they
|
|
58
58
|
# land — the linter is the gate that proves no orphan slot escapes.
|
|
59
59
|
KNOWN_PLATFORMS: set[str] = {
|
|
60
|
-
"augment", "claude", "
|
|
60
|
+
"augment", "claude", "cowork",
|
|
61
|
+
"cursor", "cline", "windsurf", "gemini", "copilot",
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
|
|
@@ -28,14 +28,37 @@ SCAN_DIRS = [
|
|
|
28
28
|
]
|
|
29
29
|
|
|
30
30
|
ALLOWED_PREFIXES = (
|
|
31
|
-
"
|
|
32
|
-
".agent-src/contexts/",
|
|
33
|
-
"agents/contexts/",
|
|
31
|
+
"contexts/", # logical name (canonical — P1.1 / P5.3)
|
|
32
|
+
".agent-src/contexts/", # projected (defensive — only seen in compressed inputs)
|
|
33
|
+
"agents/contexts/", # project-local
|
|
34
34
|
)
|
|
35
35
|
|
|
36
|
+
# `.agent-src.uncompressed/contexts/` was the legacy fully-qualified form
|
|
37
|
+
# (pre road-to-path-fixes.md P5.3). It is now a hard error: P2.1 migrated
|
|
38
|
+
# all in-tree rules to logical names, the rewriter resolves them at
|
|
39
|
+
# compress time, and the schema regex in `scripts/schemas/rule.schema.json`
|
|
40
|
+
# rejects the prefix at validate-schema time. Keeping a separate runtime
|
|
41
|
+
# diagnostic so the failure points authors at the canonical
|
|
42
|
+
# `contexts/<area>/<file>.md` form rather than a generic schema mismatch.
|
|
43
|
+
LEGACY_PREFIX = ".agent-src.uncompressed/contexts/"
|
|
44
|
+
|
|
45
|
+
# Logical names resolve against the source root.
|
|
46
|
+
SOURCE_ROOT = ROOT / ".agent-src.uncompressed"
|
|
47
|
+
|
|
36
48
|
PUBLIC_RULE_PREFIX = ".agent-src.uncompressed/rules/"
|
|
37
49
|
PROJECT_LOCAL_PREFIX = "agents/contexts/"
|
|
38
50
|
|
|
51
|
+
|
|
52
|
+
def resolve_entry(entry: str) -> Path:
|
|
53
|
+
"""Resolve a `load_context:` entry to an absolute path on disk.
|
|
54
|
+
|
|
55
|
+
Logical names (`contexts/...`) live under `.agent-src.uncompressed/`;
|
|
56
|
+
fully-qualified entries are repo-root-relative.
|
|
57
|
+
"""
|
|
58
|
+
if entry.startswith("contexts/"):
|
|
59
|
+
return SOURCE_ROOT / entry
|
|
60
|
+
return ROOT / entry
|
|
61
|
+
|
|
39
62
|
HARD_FLOOR_RULES = {"non-destructive-by-default", "security-sensitive-stop"}
|
|
40
63
|
|
|
41
64
|
CAP_ALWAYS = 2_500
|
|
@@ -126,10 +149,17 @@ def main() -> int:
|
|
|
126
149
|
if not isinstance(entry, str) or not entry.endswith(".md"):
|
|
127
150
|
errors.append(f"{rel(f)}: entry not str ending in .md → {entry!r}")
|
|
128
151
|
continue
|
|
152
|
+
if entry.startswith(LEGACY_PREFIX):
|
|
153
|
+
logical = entry[len(".agent-src.uncompressed/"):]
|
|
154
|
+
errors.append(
|
|
155
|
+
f"{rel(f)}: legacy `.agent-src.uncompressed/` prefix in load_context → {entry} "
|
|
156
|
+
f"— use logical name `{logical}` instead (road-to-path-fixes.md P5.3)"
|
|
157
|
+
)
|
|
158
|
+
continue
|
|
129
159
|
if not entry.startswith(ALLOWED_PREFIXES):
|
|
130
160
|
errors.append(f"{rel(f)}: disallowed root → {entry}")
|
|
131
161
|
continue
|
|
132
|
-
target =
|
|
162
|
+
target = resolve_entry(entry)
|
|
133
163
|
if not target.exists():
|
|
134
164
|
errors.append(f"{rel(f)}: target missing → {entry}")
|
|
135
165
|
continue
|
|
@@ -140,7 +170,7 @@ def main() -> int:
|
|
|
140
170
|
cap = cap_for(f, fm)
|
|
141
171
|
total = len(f.read_text(encoding="utf-8"))
|
|
142
172
|
for entry in eager:
|
|
143
|
-
tgt =
|
|
173
|
+
tgt = resolve_entry(entry)
|
|
144
174
|
if tgt.exists():
|
|
145
175
|
total += len(tgt.read_text(encoding="utf-8"))
|
|
146
176
|
if total > cap:
|