@event4u/agent-config 11.0.0 → 12.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/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +173 -229
- package/agents/templates/.ai-council.yml.example +46 -3
- package/dist/agent-src/commands/agent-handoff.md +0 -1
- package/dist/agent-src/commands/agent-status.md +0 -1
- package/dist/agent-src/commands/agents/audit.md +0 -1
- package/dist/agent-src/commands/agents/init.md +0 -1
- package/dist/agent-src/commands/agents/optimize.md +0 -1
- package/dist/agent-src/commands/agents/user/accept.md +0 -1
- package/dist/agent-src/commands/agents/user/delete.md +0 -1
- package/dist/agent-src/commands/agents/user/init.md +0 -1
- package/dist/agent-src/commands/agents/user/review.md +0 -1
- package/dist/agent-src/commands/agents/user/show.md +0 -1
- package/dist/agent-src/commands/agents/user/update.md +0 -1
- package/dist/agent-src/commands/agents/user.md +0 -1
- package/dist/agent-src/commands/agents.md +0 -1
- package/dist/agent-src/commands/analytics/prune.md +0 -1
- package/dist/agent-src/commands/analytics/show.md +0 -1
- package/dist/agent-src/commands/analytics.md +0 -1
- package/dist/agent-src/commands/analyze/conformance.md +0 -1
- package/dist/agent-src/commands/analyze/decision.md +0 -1
- package/dist/agent-src/commands/analyze/inbox.md +0 -1
- package/dist/agent-src/commands/analyze/incident.md +0 -1
- package/dist/agent-src/commands/analyze/near-miss.md +0 -1
- package/dist/agent-src/commands/analyze/postmortem.md +0 -1
- package/dist/agent-src/commands/analyze/premortem.md +0 -1
- package/dist/agent-src/commands/analyze/reference-repo.md +0 -1
- package/dist/agent-src/commands/analyze.md +0 -1
- package/dist/agent-src/commands/brand/identity.md +0 -1
- package/dist/agent-src/commands/brand/review.md +0 -1
- package/dist/agent-src/commands/brand/strategy.md +0 -1
- package/dist/agent-src/commands/brand/tokens.md +0 -1
- package/dist/agent-src/commands/brand/voice.md +0 -1
- package/dist/agent-src/commands/brand.md +0 -1
- package/dist/agent-src/commands/bug/fix.md +0 -1
- package/dist/agent-src/commands/bug/investigate.md +0 -1
- package/dist/agent-src/commands/bug.md +0 -1
- package/dist/agent-src/commands/challenge-me/vision.md +0 -1
- package/dist/agent-src/commands/challenge-me/with-docs.md +0 -1
- package/dist/agent-src/commands/challenge-me.md +0 -1
- package/dist/agent-src/commands/chat-history/import.md +0 -1
- package/dist/agent-src/commands/chat-history.md +0 -1
- package/dist/agent-src/commands/check-current-md.md +0 -1
- package/dist/agent-src/commands/commit/in-chunks.md +0 -1
- package/dist/agent-src/commands/commit.md +0 -1
- package/dist/agent-src/commands/condense.md +0 -1
- package/dist/agent-src/commands/context/create.md +0 -1
- package/dist/agent-src/commands/context/refactor.md +0 -1
- package/dist/agent-src/commands/context.md +0 -1
- package/dist/agent-src/commands/contribution-precheck.md +0 -1
- package/dist/agent-src/commands/cost/profile.md +0 -1
- package/dist/agent-src/commands/cost/report.md +0 -1
- package/dist/agent-src/commands/cost.md +0 -1
- package/dist/agent-src/commands/council/analysis.md +0 -1
- package/dist/agent-src/commands/council/debate.md +0 -1
- package/dist/agent-src/commands/council/default.md +0 -1
- package/dist/agent-src/commands/council/design.md +0 -1
- package/dist/agent-src/commands/council/optimize.md +0 -1
- package/dist/agent-src/commands/council/pr.md +0 -1
- package/dist/agent-src/commands/council.md +0 -1
- package/dist/agent-src/commands/design-system/capture.md +0 -1
- package/dist/agent-src/commands/design-system/generate.md +0 -1
- package/dist/agent-src/commands/design-system/import.md +0 -1
- package/dist/agent-src/commands/design-system.md +0 -1
- package/dist/agent-src/commands/estimate-ticket.md +0 -1
- package/dist/agent-src/commands/explain-run.md +0 -1
- package/dist/agent-src/commands/feature/dev.md +0 -1
- package/dist/agent-src/commands/feature/explore.md +0 -1
- package/dist/agent-src/commands/feature/plan.md +0 -1
- package/dist/agent-src/commands/feature/refactor.md +0 -1
- package/dist/agent-src/commands/feature/roadmap.md +0 -1
- package/dist/agent-src/commands/feature.md +0 -1
- package/dist/agent-src/commands/fix/ci.md +0 -1
- package/dist/agent-src/commands/fix/comments.md +0 -1
- package/dist/agent-src/commands/fix/portability.md +0 -1
- package/dist/agent-src/commands/fix/pr-comments-loop.md +0 -1
- package/dist/agent-src/commands/fix/pr-comments.md +0 -1
- package/dist/agent-src/commands/fix/quality.md +0 -1
- package/dist/agent-src/commands/fix/refs.md +0 -1
- package/dist/agent-src/commands/fix/route.md +0 -1
- package/dist/agent-src/commands/fix/seeder.md +0 -1
- package/dist/agent-src/commands/fix.md +0 -1
- package/dist/agent-src/commands/ghostwriter/delete.md +0 -1
- package/dist/agent-src/commands/ghostwriter/fetch.md +0 -1
- package/dist/agent-src/commands/ghostwriter/list.md +0 -1
- package/dist/agent-src/commands/ghostwriter/show.md +0 -1
- package/dist/agent-src/commands/ghostwriter/write.md +0 -1
- package/dist/agent-src/commands/ghostwriter.md +0 -1
- package/dist/agent-src/commands/grill-me.md +0 -1
- package/dist/agent-src/commands/humanize.md +0 -1
- package/dist/agent-src/commands/image/analyse.md +0 -1
- package/dist/agent-src/commands/image/create.md +0 -1
- package/dist/agent-src/commands/image/verify.md +0 -1
- package/dist/agent-src/commands/image.md +0 -1
- package/dist/agent-src/commands/implement-ticket.md +0 -1
- package/dist/agent-src/commands/jira-ticket.md +0 -1
- package/dist/agent-src/commands/judge/on-diff.md +0 -1
- package/dist/agent-src/commands/judge/solo.md +0 -1
- package/dist/agent-src/commands/judge/steps.md +0 -1
- package/dist/agent-src/commands/judge.md +0 -1
- package/dist/agent-src/commands/knowledge/cross-repo.md +0 -1
- package/dist/agent-src/commands/knowledge/forget.md +0 -1
- package/dist/agent-src/commands/knowledge/ingest.md +0 -1
- package/dist/agent-src/commands/knowledge/list.md +0 -1
- package/dist/agent-src/commands/knowledge.md +0 -1
- package/dist/agent-src/commands/memory/add.md +0 -1
- package/dist/agent-src/commands/memory/learn-low-impact.md +0 -1
- package/dist/agent-src/commands/memory/load.md +0 -1
- package/dist/agent-src/commands/memory/mine-session.md +0 -1
- package/dist/agent-src/commands/memory/promote.md +0 -1
- package/dist/agent-src/commands/memory/propose.md +0 -1
- package/dist/agent-src/commands/memory.md +0 -1
- package/dist/agent-src/commands/mission/upgrade.md +0 -1
- package/dist/agent-src/commands/mode.md +0 -1
- package/dist/agent-src/commands/module/create.md +0 -1
- package/dist/agent-src/commands/module/explore.md +0 -1
- package/dist/agent-src/commands/module.md +0 -1
- package/dist/agent-src/commands/optimize/agents-dir.md +0 -1
- package/dist/agent-src/commands/optimize/augmentignore.md +0 -1
- package/dist/agent-src/commands/optimize/deep.md +0 -1
- package/dist/agent-src/commands/optimize/project.md +0 -1
- package/dist/agent-src/commands/optimize/prompt.md +0 -1
- package/dist/agent-src/commands/optimize/rtk.md +0 -1
- package/dist/agent-src/commands/optimize/skills.md +0 -1
- package/dist/agent-src/commands/optimize.md +0 -1
- package/dist/agent-src/commands/orchestrate.md +0 -1
- package/dist/agent-src/commands/override/create.md +0 -1
- package/dist/agent-src/commands/override/manage.md +0 -1
- package/dist/agent-src/commands/override.md +0 -1
- package/dist/agent-src/commands/package/reset.md +0 -1
- package/dist/agent-src/commands/package/test.md +0 -1
- package/dist/agent-src/commands/package.md +0 -1
- package/dist/agent-src/commands/post-as/ghostwriter.md +0 -1
- package/dist/agent-src/commands/post-as/me.md +0 -1
- package/dist/agent-src/commands/post-as.md +0 -1
- package/dist/agent-src/commands/pr/create/description-only.md +0 -1
- package/dist/agent-src/commands/pr/create.md +23 -9
- package/dist/agent-src/commands/prediction-pool.md +0 -1
- package/dist/agent-src/commands/prepare-for-review.md +0 -1
- package/dist/agent-src/commands/profile/activate.md +0 -1
- package/dist/agent-src/commands/profile/deactivate.md +0 -1
- package/dist/agent-src/commands/profile/show.md +0 -1
- package/dist/agent-src/commands/profile.md +0 -1
- package/dist/agent-src/commands/project/analyze.md +0 -1
- package/dist/agent-src/commands/project/health.md +0 -1
- package/dist/agent-src/commands/project.md +0 -1
- package/dist/agent-src/commands/refine-ticket.md +0 -1
- package/dist/agent-src/commands/research/deep.md +0 -1
- package/dist/agent-src/commands/research/report.md +0 -1
- package/dist/agent-src/commands/research.md +0 -1
- package/dist/agent-src/commands/review/changes.md +0 -1
- package/dist/agent-src/commands/review/routing.md +0 -1
- package/dist/agent-src/commands/review.md +0 -1
- package/dist/agent-src/commands/roadmap/ai-council.md +0 -1
- package/dist/agent-src/commands/roadmap/create.md +0 -1
- package/dist/agent-src/commands/roadmap/materialize.md +0 -1
- package/dist/agent-src/commands/roadmap/next.md +0 -1
- package/dist/agent-src/commands/roadmap/process-full.md +0 -1
- package/dist/agent-src/commands/roadmap/process-phase.md +0 -1
- package/dist/agent-src/commands/roadmap/process-step.md +0 -1
- package/dist/agent-src/commands/roadmap.md +0 -1
- package/dist/agent-src/commands/rule-compliance-audit.md +0 -1
- package/dist/agent-src/commands/security-audit-config.md +0 -1
- package/dist/agent-src/commands/skill/preview.md +0 -1
- package/dist/agent-src/commands/skill.md +0 -1
- package/dist/agent-src/commands/skills/discover.md +0 -1
- package/dist/agent-src/commands/skills.md +0 -1
- package/dist/agent-src/commands/sync/agent-settings.md +0 -1
- package/dist/agent-src/commands/sync/gitignore/fix.md +0 -1
- package/dist/agent-src/commands/sync/gitignore.md +0 -1
- package/dist/agent-src/commands/sync.md +0 -1
- package/dist/agent-src/commands/tdd/green.md +0 -1
- package/dist/agent-src/commands/tdd/red.md +0 -1
- package/dist/agent-src/commands/tdd/refactor.md +0 -1
- package/dist/agent-src/commands/tdd.md +0 -1
- package/dist/agent-src/commands/team/adversarial.md +0 -1
- package/dist/agent-src/commands/team/delegate.md +0 -1
- package/dist/agent-src/commands/team/review.md +0 -1
- package/dist/agent-src/commands/team/status.md +0 -1
- package/dist/agent-src/commands/team-knowledge/bootstrap.md +0 -1
- package/dist/agent-src/commands/team-knowledge/consolidate.md +0 -1
- package/dist/agent-src/commands/team-knowledge.md +0 -1
- package/dist/agent-src/commands/team.md +0 -1
- package/dist/agent-src/commands/tests/create.md +0 -1
- package/dist/agent-src/commands/tests/e2e-heal.md +0 -1
- package/dist/agent-src/commands/tests/e2e-plan.md +0 -1
- package/dist/agent-src/commands/tests/execute.md +0 -1
- package/dist/agent-src/commands/tests.md +0 -1
- package/dist/agent-src/commands/threat-model.md +0 -1
- package/dist/agent-src/commands/update-form-request-messages.md +0 -1
- package/dist/agent-src/commands/upstream-contribute.md +0 -1
- package/dist/agent-src/commands/video/from-script.md +0 -1
- package/dist/agent-src/commands/video/from-song.md +0 -1
- package/dist/agent-src/commands/video/scene.md +0 -1
- package/dist/agent-src/commands/video/stitch.md +0 -1
- package/dist/agent-src/commands/video/storyboard.md +0 -1
- package/dist/agent-src/commands/video.md +0 -1
- package/dist/agent-src/commands/work.md +0 -1
- package/dist/agent-src/commands/worktree/cleanup.md +0 -1
- package/dist/agent-src/commands/worktree/create.md +0 -1
- package/dist/agent-src/commands/worktree/status.md +0 -1
- package/dist/agent-src/commands/worktree/verify.md +0 -1
- package/dist/agent-src/commands/worktree.md +0 -1
- package/dist/agent-src/contexts/model-recommendations.md +6 -4
- package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +1 -0
- package/dist/agent-src/scripts/roadmap_gates.ts +30 -2
- package/dist/agent-src/scripts/update_roadmap_progress.ts +32 -1
- package/dist/agent-src/skills/docx-authoring/SKILL.md +1 -0
- package/dist/agent-src/skills/git-workflow/SKILL.md +9 -1
- package/dist/agent-src/skills/pdf-tools/SKILL.md +1 -0
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +7 -7
- package/dist/agent-src/skills/skill-writing/SKILL.md +35 -0
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/roadmaps.md +41 -3
- package/dist/cli/commands/commands.candidates.test.js +1 -1
- package/dist/cli/commands/commands.candidates.test.js.map +1 -1
- package/dist/cli/commands/commands.js +9 -15
- package/dist/cli/commands/commands.js.map +1 -1
- package/dist/cli/commands/profiles.test.js +7 -7
- package/dist/cli/commands/profiles.test.js.map +1 -1
- package/dist/cli/discovery/loadManifest.js.map +1 -1
- package/dist/cli/main.js +2 -2
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/registry.js +1 -0
- package/dist/cli/registry.js.map +1 -1
- package/dist/cli-delegate/chunk-K34QTSD5.js +3895 -0
- package/dist/cli-delegate/{chunk-L565YFXH.js → chunk-LKZ5DOYW.js} +557 -297
- package/dist/cli-delegate/{chunk-NN4KW4HZ.js → chunk-O52B4F5S.js} +123 -2338
- package/dist/cli-delegate/cmd_conformance.js +18 -6
- package/dist/cli-delegate/cmd_doctor.js +2 -2
- package/dist/cli-delegate/cmd_export.js +1 -1
- package/dist/cli-delegate/cmd_handoff.js +1 -1
- package/dist/cli-delegate/cmd_prune.js +1 -1
- package/dist/cli-delegate/cmd_settings_migrate.js +1 -1
- package/dist/cli-delegate/cmd_sync.js +1 -1
- package/dist/cli-delegate/cmd_uninstall.js +1 -1
- package/dist/cli-delegate/cmd_validate.js +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +213 -412
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +2 -2
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +3 -3
- package/dist/discovery/trust-report.md +1 -1
- package/dist/discovery/workspaces.json +3 -3
- package/dist/hooks/dispatch.js +2588 -1082
- package/dist/install/install.mjs +744 -482
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +159 -15
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/model_tier.js +9 -0
- package/dist/scripts/_lib/model_tier.js.map +1 -1
- package/dist/scripts/condense.js +50 -0
- package/dist/scripts/condense.js.map +1 -1
- package/docs/MIGRATION.md +21 -1
- package/docs/contracts/settings-classes.md +41 -0
- package/package.json +1 -1
- package/src/agent-src/scripts/roadmap_gates.ts +30 -2
- package/src/agent-src/scripts/update_roadmap_progress.ts +32 -1
- package/src/config/agent-settings.template.yml +34 -0
- package/src/config/evaluator-budgets.json +4 -4
- package/src/config/gate-coverage.yml +39 -9
- package/src/config/gate-violation-baselines.json +9 -3
- package/src/config/hook-token-budget.json +7 -1
- package/src/config/preamble-payload-budget.json +28 -0
- package/src/scripts/_dispatch.bash +52 -5
- package/src/scripts/_lib/git_common_dir.ts +173 -0
- package/src/scripts/_lib/model_tier.ts +9 -0
- package/src/scripts/_lib/release_highlights.ts +108 -7
- package/src/scripts/_lib/skill_catalogue.ts +862 -0
- package/src/scripts/ai_council/clients.ts +182 -3
- package/src/scripts/ai_council/config.ts +16 -1
- package/src/scripts/ai_council/low_impact.ts +14 -1
- package/src/scripts/ai_council/orchestrator.ts +14 -1
- package/src/scripts/ai_council/transport_resolver.ts +32 -2
- package/src/scripts/audit_command_surface.ts +21 -15
- package/src/scripts/build_catalog_index.ts +1 -1
- package/src/scripts/build_discovery_manifest.ts +30 -34
- package/src/scripts/capture_skill_catalogue.ts +185 -328
- package/src/scripts/check_branch_freshness.ts +217 -62
- package/src/scripts/check_release_published.ts +6 -2
- package/src/scripts/check_release_trunk_sync.ts +18 -2
- package/src/scripts/condense.ts +50 -1
- package/src/scripts/dispatch_r2_reviewer.ts +15 -5
- package/src/scripts/evidence_report.ts +15 -4
- package/src/scripts/gen_discovery_baseline.ts +3 -3
- package/src/scripts/hook_manifest.yaml +67 -13
- package/src/scripts/hooks/concern_registry.ts +2 -0
- package/src/scripts/hooks/orchestration_record_hook.ts +21 -0
- package/src/scripts/hooks/subagent_ledger_hook.ts +17 -4
- package/src/scripts/hooks/tool_result_bytes_hook.ts +219 -0
- package/src/scripts/hot_context_hook.ts +13 -1
- package/src/scripts/install.ts +129 -0
- package/src/scripts/janitor.ts +77 -2
- package/src/scripts/lint_command_tiers.ts +29 -75
- package/src/scripts/lint_originality.ts +101 -14
- package/src/scripts/lint_plan_risk_register.ts +22 -9
- package/src/scripts/lint_roadmap_blockers.ts +109 -6
- package/src/scripts/lint_scheduled_deprecations.ts +738 -0
- package/src/scripts/migration_status.ts +17 -1
- package/src/scripts/probe_review_binding_drift.ts +731 -0
- package/src/scripts/release.ts +95 -1
- package/src/scripts/schemas/command.schema.json +6 -11
- package/src/scripts/schemas/skill.schema.json +12 -2
- package/src/scripts/schemas/subagent.schema.json +2 -2
- package/src/scripts/session_eol_report.ts +81 -0
- package/src/scripts/skill_linter.ts +82 -2
- package/src/scripts/workspace_doctor.ts +464 -0
- package/src/scripts/worktree_cleanup_check.ts +113 -8
- package/dist/cli-delegate/chunk-AYKQGIBG.js +0 -1594
package/src/scripts/janitor.ts
CHANGED
|
@@ -24,22 +24,82 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
|
24
24
|
const _HERE = fileURLToPath(import.meta.url);
|
|
25
25
|
const REPO_ROOT = path.join(path.dirname(_HERE), '..', '..');
|
|
26
26
|
|
|
27
|
-
// TTL configuration per directory (days)
|
|
28
|
-
|
|
27
|
+
// TTL configuration per directory (days).
|
|
28
|
+
//
|
|
29
|
+
// `owner` / `reviewBy` (road-to-inbox-harvest-2026-08-d-context-ledger Step
|
|
30
|
+
// 4.1) put every TTL under the same discipline the two budget config files
|
|
31
|
+
// carry: a retention number nobody owns and nobody revisits is a guess that
|
|
32
|
+
// hardens into a fact. The falsifier is printed per run — see the reclaim
|
|
33
|
+
// total in `main` — so a TTL that reclaims nothing over a review window is
|
|
34
|
+
// removed rather than defended.
|
|
35
|
+
const TTL_CONFIG: ReadonlyArray<{
|
|
36
|
+
dir: string;
|
|
37
|
+
ttlDays: number;
|
|
38
|
+
description: string;
|
|
39
|
+
owner: string;
|
|
40
|
+
reviewBy: string;
|
|
41
|
+
}> = [
|
|
29
42
|
{
|
|
30
43
|
dir: 'agents/tmp.old',
|
|
31
44
|
ttlDays: 30,
|
|
32
45
|
description: 'Processed inbox archive (consumed agents/tmp/ files)',
|
|
46
|
+
owner: 'maintainer',
|
|
47
|
+
reviewBy: '2026-11-10',
|
|
33
48
|
},
|
|
34
49
|
{
|
|
35
50
|
dir: 'agents/runtime/tmp',
|
|
36
51
|
ttlDays: 7,
|
|
37
52
|
description: 'Agent scratch / runtime tmp',
|
|
53
|
+
owner: 'maintainer',
|
|
54
|
+
reviewBy: '2026-11-10',
|
|
38
55
|
},
|
|
39
56
|
{
|
|
40
57
|
dir: 'agents/runtime/council/responses',
|
|
41
58
|
ttlDays: 7,
|
|
42
59
|
description: 'Council response cache',
|
|
60
|
+
owner: 'maintainer',
|
|
61
|
+
reviewBy: '2026-11-10',
|
|
62
|
+
},
|
|
63
|
+
// ── Session-scoped runtime state (Step 4.1) ────────────────────────────
|
|
64
|
+
//
|
|
65
|
+
// These four directories accrete ONE entry per session behind a
|
|
66
|
+
// once-per-session latch, and nothing in the tree removed them: writers
|
|
67
|
+
// and readers existed, no reaper did. They are named INDIVIDUALLY rather
|
|
68
|
+
// than sweeping `agents/runtime/state` as a whole, and that is the
|
|
69
|
+
// load-bearing decision in this step.
|
|
70
|
+
//
|
|
71
|
+
// `agents/runtime/state` also holds the CUMULATIVE measurement streams —
|
|
72
|
+
// `audit/`, `injection-census.jsonl`, `tool-result-census.jsonl`,
|
|
73
|
+
// `ai-video-telemetry.jsonl`, the memory index — and a directory-level
|
|
74
|
+
// TTL would delete exactly the corpora this roadmap exists to build. A
|
|
75
|
+
// sweeper that eats the instrument's own data is worse than no sweeper.
|
|
76
|
+
{
|
|
77
|
+
dir: 'agents/runtime/state/session-eol',
|
|
78
|
+
ttlDays: 7,
|
|
79
|
+
description: 'Per-session end-of-life counters (one file per session)',
|
|
80
|
+
owner: 'maintainer',
|
|
81
|
+
reviewBy: '2026-11-10',
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
dir: 'agents/runtime/state/reread-guard',
|
|
85
|
+
ttlDays: 7,
|
|
86
|
+
description: 'Per-session re-read latch',
|
|
87
|
+
owner: 'maintainer',
|
|
88
|
+
reviewBy: '2026-11-10',
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
dir: 'agents/runtime/state/end-review-nudge',
|
|
92
|
+
ttlDays: 7,
|
|
93
|
+
description: 'Per-session end-review latch',
|
|
94
|
+
owner: 'maintainer',
|
|
95
|
+
reviewBy: '2026-11-10',
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
dir: 'agents/runtime/state/probe-throttle',
|
|
99
|
+
ttlDays: 7,
|
|
100
|
+
description: 'Per-session probe throttle latch',
|
|
101
|
+
owner: 'maintainer',
|
|
102
|
+
reviewBy: '2026-11-10',
|
|
43
103
|
},
|
|
44
104
|
];
|
|
45
105
|
|
|
@@ -227,6 +287,21 @@ function report(apply: boolean): void {
|
|
|
227
287
|
);
|
|
228
288
|
} else if (apply && totalExpired > 0) {
|
|
229
289
|
process.stdout.write(`Total deleted: ${totalExpired} file(s), ${_humanSize(totalSize)} freed.\n`);
|
|
290
|
+
} else {
|
|
291
|
+
// road-to-inbox-harvest-2026-08-d-context-ledger Step 4.2 — the TTL's
|
|
292
|
+
// own falsifier, and it only works if a ZERO is printed.
|
|
293
|
+
//
|
|
294
|
+
// Until now a run that reclaimed nothing printed no total at all, so
|
|
295
|
+
// "the sweeper ran and found nothing expired" and "the sweeper did not
|
|
296
|
+
// run" produced byte-identical output. That is the difference between
|
|
297
|
+
// evidence that a retention policy is unnecessary and no evidence at
|
|
298
|
+
// all — and the first is what removes a TTL entry rather than letting
|
|
299
|
+
// it harden into an unexamined default.
|
|
300
|
+
process.stdout.write(
|
|
301
|
+
`Total ${apply ? 'deleted' : 'reclaimable'}: 0 file(s), 0 B — nothing was past its TTL.\n` +
|
|
302
|
+
`A reclaim that stays at zero across a review window is the signal to drop the ` +
|
|
303
|
+
`TTL entry, not to keep it: see the owner / reviewBy fields on each entry.\n`,
|
|
304
|
+
);
|
|
230
305
|
}
|
|
231
306
|
}
|
|
232
307
|
|
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* Lint slash-command frontmatter for the `
|
|
3
|
+
* Lint slash-command frontmatter for the `visibility:` key.
|
|
4
4
|
*
|
|
5
5
|
* Ported from the retired Python `src/scripts/lint_command_tiers.py` (ADR-200, Phase 4 /
|
|
6
|
-
* Wave 4b).
|
|
7
|
-
* ordering,
|
|
8
|
-
*
|
|
9
|
-
* condensed-projection passes).
|
|
6
|
+
* Wave 4b). Keeps the Python CLI contract's shape: same scan scope, file
|
|
7
|
+
* ordering, stdout/stderr split, and exit codes (return-code bitwise-OR
|
|
8
|
+
* accumulation across the domain + per-root + condensed-projection passes).
|
|
10
9
|
*
|
|
11
|
-
*
|
|
12
|
-
* tier
|
|
10
|
+
* The integer `tier:` alias this file was named for is GONE
|
|
11
|
+
* (road-to-tier-removal Phase 4): `visibility:` is now the sole command-surface
|
|
12
|
+
* classifier. The filename is retained because the gate id, its Taskfile entry,
|
|
13
|
+
* and the contract doc all key on it; only the checked key changed.
|
|
14
|
+
*
|
|
15
|
+
* Hard-fails CI if any command lacks a `visibility:` declaration or uses an
|
|
16
|
+
* unknown value. The valid set is locked by
|
|
13
17
|
* docs/contracts/command-surface-tiers.md.
|
|
14
18
|
*
|
|
15
19
|
* Exit codes:
|
|
16
|
-
* 0 every command declares a valid
|
|
17
|
-
* 1 one or more commands missing or using an invalid
|
|
20
|
+
* 0 every command declares a valid visibility
|
|
21
|
+
* 1 one or more commands missing or using an invalid visibility
|
|
18
22
|
*/
|
|
19
23
|
import fs from "node:fs";
|
|
20
24
|
import path from "node:path";
|
|
@@ -79,20 +83,13 @@ function COMMANDS_DIRS(): string[] {
|
|
|
79
83
|
}
|
|
80
84
|
|
|
81
85
|
const DOMAINS_DIR = path.join(REPO, "src", "domains");
|
|
82
|
-
// Consumer-facing projection — must also carry
|
|
83
|
-
// (which symlinks to dist/agent-src/commands/) renders the
|
|
86
|
+
// Consumer-facing projection — must also carry visibility so .augment/commands/
|
|
87
|
+
// (which symlinks to dist/agent-src/commands/) renders the surface filter.
|
|
84
88
|
const COMMANDS_DIR_CONDENSED = path.join(REPO, "dist/agent-src", "commands");
|
|
85
89
|
|
|
86
|
-
|
|
87
|
-
//
|
|
88
|
-
// back-compat integer alias. Both are validated; when both are present they
|
|
89
|
-
// MUST agree per this mapping.
|
|
90
|
+
// ADR-092 named `visibility:` the source of truth; road-to-tier-removal Phase 4
|
|
91
|
+
// removed the integer `tier:` alias, so this set is the whole vocabulary.
|
|
90
92
|
const VALID_VISIBILITIES: ReadonlySet<string> = new Set(["visible", "advanced", "internal"]);
|
|
91
|
-
const TIER_TO_VISIBILITY: Readonly<Record<string, string>> = {
|
|
92
|
-
"0": "visible",
|
|
93
|
-
"1": "advanced",
|
|
94
|
-
"2": "internal",
|
|
95
|
-
};
|
|
96
93
|
|
|
97
94
|
export function parse_field(text: string, key: string): string | null {
|
|
98
95
|
if (!text.startsWith("---\n")) {
|
|
@@ -116,15 +113,13 @@ export function parse_field(text: string, key: string): string | null {
|
|
|
116
113
|
return null;
|
|
117
114
|
}
|
|
118
115
|
|
|
119
|
-
export function parse_tier(text: string): string | null {
|
|
120
|
-
return parse_field(text, "tier");
|
|
121
|
-
}
|
|
122
|
-
|
|
123
116
|
/**
|
|
124
117
|
* Validate the visibility field (ADR-092). Returns an error string or null.
|
|
125
118
|
*
|
|
126
|
-
* Requires a present + valid visibility
|
|
127
|
-
*
|
|
119
|
+
* Requires a present + valid visibility. The tier↔visibility consistency clause
|
|
120
|
+
* is gone with the alias itself (road-to-tier-removal Phase 4) — a stray `tier:`
|
|
121
|
+
* key is now rejected by `command.schema.json` (`additionalProperties: false`),
|
|
122
|
+
* not reconciled here.
|
|
128
123
|
*/
|
|
129
124
|
export function visibility_error(text: string): string | null {
|
|
130
125
|
const vis = parse_field(text, "visibility");
|
|
@@ -134,10 +129,6 @@ export function visibility_error(text: string): string | null {
|
|
|
134
129
|
if (!VALID_VISIBILITIES.has(vis)) {
|
|
135
130
|
return `invalid visibility '${vis}'`;
|
|
136
131
|
}
|
|
137
|
-
const tier = parse_field(text, "tier");
|
|
138
|
-
if (tier !== null && tier in TIER_TO_VISIBILITY && TIER_TO_VISIBILITY[tier] !== vis) {
|
|
139
|
-
return `visibility '${vis}' disagrees with tier '${tier}' (expected '${TIER_TO_VISIBILITY[tier]}')`;
|
|
140
|
-
}
|
|
141
132
|
return null;
|
|
142
133
|
}
|
|
143
134
|
|
|
@@ -153,12 +144,6 @@ function stripChar(s: string, ch: string): string {
|
|
|
153
144
|
return s.slice(start, end);
|
|
154
145
|
}
|
|
155
146
|
|
|
156
|
-
// Mirror Python's `print(sorted(set))` list repr: ['0', '1', ...].
|
|
157
|
-
function sortedTiersRepr(): string {
|
|
158
|
-
const items = [...VALID_TIERS].sort();
|
|
159
|
-
return `[${items.map((s) => `'${s}'`).join(", ")}]`;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
147
|
// Mirror Python's `print(sorted(VALID_VISIBILITIES))` list repr.
|
|
163
148
|
function sortedVisibilitiesRepr(): string {
|
|
164
149
|
const items = [...VALID_VISIBILITIES].sort();
|
|
@@ -184,43 +169,27 @@ export function lint(commands_dir: string, quiet = false): number {
|
|
|
184
169
|
return 1;
|
|
185
170
|
}
|
|
186
171
|
|
|
187
|
-
const missing: string[] = [];
|
|
188
|
-
const invalid: Array<[string, string]> = [];
|
|
189
172
|
const vis_errors: Array<[string, string]> = [];
|
|
190
173
|
|
|
191
174
|
for (const cmd of commands) {
|
|
192
175
|
const rel = relPosix(cmd, commands_dir);
|
|
193
176
|
const text = fs.readFileSync(cmd, "utf-8");
|
|
194
|
-
const tier = parse_tier(text);
|
|
195
|
-
if (tier === null) {
|
|
196
|
-
missing.push(rel);
|
|
197
|
-
} else if (!VALID_TIERS.has(tier)) {
|
|
198
|
-
invalid.push([rel, tier]);
|
|
199
|
-
}
|
|
200
177
|
const ve = visibility_error(text);
|
|
201
178
|
if (ve !== null) {
|
|
202
179
|
vis_errors.push([rel, ve]);
|
|
203
180
|
}
|
|
204
181
|
}
|
|
205
182
|
|
|
206
|
-
if (
|
|
183
|
+
if (vis_errors.length > 0) {
|
|
207
184
|
process.stderr.write(
|
|
208
|
-
`❌ lint_command_tiers: ${
|
|
209
|
-
`${invalid.length} invalid tier, ${vis_errors.length} visibility ` +
|
|
185
|
+
`❌ lint_command_tiers: ${vis_errors.length} visibility ` +
|
|
210
186
|
`(of ${commands.length} commands)\n`,
|
|
211
187
|
);
|
|
212
|
-
for (const name of missing) {
|
|
213
|
-
process.stderr.write(` missing tier: ${name}\n`);
|
|
214
|
-
}
|
|
215
|
-
for (const [name, tier] of invalid) {
|
|
216
|
-
process.stderr.write(` invalid tier '${tier}': ${name}\n`);
|
|
217
|
-
}
|
|
218
188
|
for (const [name, err] of vis_errors) {
|
|
219
189
|
process.stderr.write(` ${err}: ${name}\n`);
|
|
220
190
|
}
|
|
221
191
|
process.stderr.write(
|
|
222
|
-
` valid
|
|
223
|
-
`valid visibility: ${sortedVisibilitiesRepr()}\n`,
|
|
192
|
+
` valid visibility: ${sortedVisibilitiesRepr()}\n`,
|
|
224
193
|
);
|
|
225
194
|
process.stderr.write(
|
|
226
195
|
" contract: docs/contracts/command-surface-tiers.md (ADR-092)\n",
|
|
@@ -231,7 +200,7 @@ export function lint(commands_dir: string, quiet = false): number {
|
|
|
231
200
|
if (!quiet) {
|
|
232
201
|
process.stdout.write(
|
|
233
202
|
`✅ lint_command_tiers: ${commands.length} commands, ` +
|
|
234
|
-
"all
|
|
203
|
+
"all visibility values valid\n",
|
|
235
204
|
);
|
|
236
205
|
}
|
|
237
206
|
return 0;
|
|
@@ -246,35 +215,20 @@ export function lint_domain_sources(quiet = false): number {
|
|
|
246
215
|
);
|
|
247
216
|
return 1;
|
|
248
217
|
}
|
|
249
|
-
const missing: string[] = [];
|
|
250
|
-
const invalid: Array<[string, string]> = [];
|
|
251
218
|
const vis_errors: Array<[string, string]> = [];
|
|
252
219
|
for (const c of commands) {
|
|
253
220
|
const rel = relPosix(c, REPO);
|
|
254
221
|
const text = fs.readFileSync(c, "utf-8");
|
|
255
|
-
const t = parse_tier(text);
|
|
256
|
-
if (t === null) {
|
|
257
|
-
missing.push(rel);
|
|
258
|
-
} else if (!VALID_TIERS.has(t)) {
|
|
259
|
-
invalid.push([rel, t]);
|
|
260
|
-
}
|
|
261
222
|
const ve = visibility_error(text);
|
|
262
223
|
if (ve !== null) {
|
|
263
224
|
vis_errors.push([rel, ve]);
|
|
264
225
|
}
|
|
265
226
|
}
|
|
266
|
-
if (
|
|
227
|
+
if (vis_errors.length > 0) {
|
|
267
228
|
process.stderr.write(
|
|
268
|
-
`❌ lint_command_tiers: ${
|
|
269
|
-
`${invalid.length} invalid tier, ${vis_errors.length} visibility ` +
|
|
229
|
+
`❌ lint_command_tiers: ${vis_errors.length} visibility ` +
|
|
270
230
|
`(of ${commands.length} domain commands)\n`,
|
|
271
231
|
);
|
|
272
|
-
for (const name of missing) {
|
|
273
|
-
process.stderr.write(` missing tier: ${name}\n`);
|
|
274
|
-
}
|
|
275
|
-
for (const [name, tier] of invalid) {
|
|
276
|
-
process.stderr.write(` invalid tier '${tier}': ${name}\n`);
|
|
277
|
-
}
|
|
278
232
|
for (const [name, err] of vis_errors) {
|
|
279
233
|
process.stderr.write(` ${err}: ${name}\n`);
|
|
280
234
|
}
|
|
@@ -283,7 +237,7 @@ export function lint_domain_sources(quiet = false): number {
|
|
|
283
237
|
if (!quiet) {
|
|
284
238
|
process.stdout.write(
|
|
285
239
|
`✅ lint_command_tiers: ${commands.length} domain commands, ` +
|
|
286
|
-
"all
|
|
240
|
+
"all visibility values valid\n",
|
|
287
241
|
);
|
|
288
242
|
}
|
|
289
243
|
return 0;
|
|
@@ -354,7 +308,7 @@ export function main(): number {
|
|
|
354
308
|
}
|
|
355
309
|
// The condensed projection is the consumer-facing tree (via the
|
|
356
310
|
// .augment/commands → dist/agent-src/commands symlink). It must also
|
|
357
|
-
// carry
|
|
311
|
+
// carry visibility so the surface stays uniform.
|
|
358
312
|
if (_isDir(COMMANDS_DIR_CONDENSED)) {
|
|
359
313
|
rc |= lint(COMMANDS_DIR_CONDENSED, QUIET);
|
|
360
314
|
}
|
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
*
|
|
21
21
|
* Modes:
|
|
22
22
|
* (no args) full pairwise audit → agents/reports/originality.{json,md}
|
|
23
|
+
* --base <ref> the revision the change set is measured against; defines
|
|
24
|
+
* what counts as ESTABLISHED scaffold (see below)
|
|
23
25
|
* --changed <f...> CI path: each changed file vs the whole same-class corpus
|
|
24
26
|
* AND vs the other changed files of its class
|
|
25
27
|
*
|
|
@@ -41,7 +43,7 @@
|
|
|
41
43
|
* it drifts as commands/skills are added or removed. Re-run the full audit
|
|
42
44
|
* after any large corpus change; do not treat 40 % as invariant.
|
|
43
45
|
* 2. Adversarial batch masking. In `--changed` mode the boilerplate set is
|
|
44
|
-
*
|
|
46
|
+
* derived from the ESTABLISHED corpus, so a batch of ≥ _dfFloor
|
|
45
47
|
* near-identical re-skins submitted together cannot classify its own shared
|
|
46
48
|
* shingles as boilerplate (see `_changed`). The full-audit sweep (no
|
|
47
49
|
* "changed" notion) does NOT have this guard — a batch of ≥ floor identical
|
|
@@ -51,8 +53,35 @@
|
|
|
51
53
|
* computes the PR's changed corpus files and runs `--changed` on them
|
|
52
54
|
* (empty changed set reports INCONCLUSIVE, never a pass).
|
|
53
55
|
*
|
|
56
|
+
* HOW "established" is resolved, and why it changed (2026-08-14):
|
|
57
|
+
* With `--base <ref>` the established corpus is the class corpus AS IT
|
|
58
|
+
* EXISTS AT THAT REVISION — content read from git, not from the worktree.
|
|
59
|
+
* Without it, the older approximation applies: on-disk corpus MINUS the
|
|
60
|
+
* change set.
|
|
61
|
+
*
|
|
62
|
+
* That approximation has a degeneracy the base-ref form does not. When a
|
|
63
|
+
* change set covers (nearly) the whole class — a mechanical edit such as
|
|
64
|
+
* dropping one frontmatter key from every command — "corpus minus change
|
|
65
|
+
* set" is EMPTY, no shingle clears the DF floor, nothing is scaffold, and
|
|
66
|
+
* the class's shared skeleton is scored as authored overlap. Measured on
|
|
67
|
+
* road-to-tier-removal Phase 4: the full audit reported worst 40 % / 0
|
|
68
|
+
* failures, while `--changed` over the same 200 unmodified-in-substance
|
|
69
|
+
* files reported 67.9 % and 3 failures — byte-identical on the base
|
|
70
|
+
* revision's own content, so the delta was entirely the masking collapse.
|
|
71
|
+
*
|
|
72
|
+
* The base-ref form does NOT weaken the guard, and it is worth being
|
|
73
|
+
* precise about why rather than claiming more. Both forms close the
|
|
74
|
+
* re-skin-batch axis, for different reasons: the old one excludes the
|
|
75
|
+
* change set outright, the new one relies on those files' shared shingles
|
|
76
|
+
* being absent from the base content (a NEW file contributes nothing at
|
|
77
|
+
* all; a REWRITTEN file contributes only its OLD text). Either way a
|
|
78
|
+
* batch cannot promote its own new shingles to scaffold. What changes is
|
|
79
|
+
* only the degenerate case: an established corpus that is empty because
|
|
80
|
+
* the change set swallowed it.
|
|
81
|
+
*
|
|
54
82
|
* Exit 0 clean/warn · 1 fail · 2 usage.
|
|
55
83
|
*/
|
|
84
|
+
import { spawnSync } from 'node:child_process';
|
|
56
85
|
import * as fs from 'node:fs';
|
|
57
86
|
import * as path from 'node:path';
|
|
58
87
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
@@ -176,7 +205,11 @@ function _templateShingles(spec: ClassSpec): Set<string> {
|
|
|
176
205
|
}
|
|
177
206
|
|
|
178
207
|
function _load(abs: string, cls: ArtifactClass, tmpl: Set<string>): Artifact {
|
|
179
|
-
|
|
208
|
+
return _loadText(fs.readFileSync(abs, 'utf-8'), abs, cls, tmpl);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** `_load` over supplied text — used to load a file's content at a base ref. */
|
|
212
|
+
function _loadText(text: string, abs: string, cls: ArtifactClass, tmpl: Set<string>): Artifact {
|
|
180
213
|
const raw = shingles(text, K);
|
|
181
214
|
// Subtract shared scaffold — a shingle also present in the class template
|
|
182
215
|
// is boilerplate, not authored content.
|
|
@@ -332,14 +365,41 @@ function _writeReport(byClass: Map<ArtifactClass, Artifact[]>, pairs: Pair[], di
|
|
|
332
365
|
|
|
333
366
|
// --- changed mode ------------------------------------------------------------
|
|
334
367
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
368
|
+
/**
|
|
369
|
+
* A corpus file's content at `ref`, or null when the file does not exist there.
|
|
370
|
+
*
|
|
371
|
+
* Null is the load-bearing case: a file absent at the base revision is NEW in
|
|
372
|
+
* this change set, so it contributes nothing to the scaffold definition. That is
|
|
373
|
+
* what keeps a batch of re-skinned newcomers from masking itself.
|
|
374
|
+
*
|
|
375
|
+
* A git failure is indistinguishable from "absent" at this layer, and both
|
|
376
|
+
* resolve the same conservative way — the file is excluded from scaffold, which
|
|
377
|
+
* can only make the gate stricter, never blinder.
|
|
378
|
+
*/
|
|
379
|
+
function _textAtRef(ref: string, relpath: string): string | null {
|
|
380
|
+
const r = spawnSync('git', ['show', `${ref}:${relpath}`], {
|
|
381
|
+
cwd: ROOT,
|
|
382
|
+
encoding: 'utf-8',
|
|
383
|
+
maxBuffer: 32 * 1024 * 1024,
|
|
384
|
+
});
|
|
385
|
+
if (r.status !== 0 || typeof r.stdout !== 'string') return null;
|
|
386
|
+
return r.stdout;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function _changed(files: string[], base: string | null = null): { pairs: Pair[]; fails: number } {
|
|
390
|
+
// The boilerplate DF set is derived from the ESTABLISHED corpus only.
|
|
391
|
+
// Without this, a batch of ≥ _dfFloor near-identical re-skins submitted
|
|
392
|
+
// together would lift its OWN shared shingles over the floor, be
|
|
393
|
+
// reclassified as boilerplate, subtracted from every copy, and score 0
|
|
394
|
+
// against each other — the gate blinds itself on exactly the attack it
|
|
395
|
+
// exists to catch. "What is scaffold" must be defined by the corpus as it
|
|
396
|
+
// stands, never by the diff proposing to change it.
|
|
397
|
+
//
|
|
398
|
+
// `--base` makes "as it stands" literal: scaffold comes from the class
|
|
399
|
+
// corpus AT THAT REVISION. Without it we fall back to the older
|
|
400
|
+
// approximation (on-disk minus the change set), which is equivalent for a
|
|
401
|
+
// small change set but degenerates to an EMPTY established corpus when the
|
|
402
|
+
// change set covers the class — see the § 2 note in the module header.
|
|
343
403
|
const changedRel = new Set(files.map((f) => _rel(path.resolve(ROOT, f))));
|
|
344
404
|
|
|
345
405
|
// Build the corpus in RAW (template-subtracted, pre-DF) form per class so we
|
|
@@ -351,8 +411,18 @@ function _changed(files: string[]): { pairs: Pair[]; fails: number } {
|
|
|
351
411
|
const tmpl = _templateShingles(spec);
|
|
352
412
|
templateCache.set(spec.name, tmpl);
|
|
353
413
|
const arts = spec.files().map((f) => _load(f, spec.name, tmpl));
|
|
354
|
-
// Established corpus
|
|
355
|
-
|
|
414
|
+
// Established corpus: at `base` when given (a file absent there is NEW
|
|
415
|
+
// and contributes no scaffold), else on-disk arts minus the change set.
|
|
416
|
+
const established =
|
|
417
|
+
base === null
|
|
418
|
+
? arts.filter((a) => !changedRel.has(a.relpath))
|
|
419
|
+
: arts
|
|
420
|
+
.map((a) => {
|
|
421
|
+
const text = _textAtRef(base, a.relpath);
|
|
422
|
+
return text === null ? null : _loadText(text, path.resolve(ROOT, a.relpath), spec.name, tmpl);
|
|
423
|
+
})
|
|
424
|
+
.filter((a): a is Artifact => a !== null);
|
|
425
|
+
const boiler = _boilerplateSet(established);
|
|
356
426
|
for (const a of arts) _subtract(a, boiler);
|
|
357
427
|
byClass.set(spec.name, arts);
|
|
358
428
|
boilerByClass.set(spec.name, boiler);
|
|
@@ -404,13 +474,30 @@ export function main(argv?: string[]): number {
|
|
|
404
474
|
}
|
|
405
475
|
let quiet = false;
|
|
406
476
|
let changedFiles: string[] | null = null;
|
|
477
|
+
let base: string | null = null;
|
|
407
478
|
for (let i = 0; i < args.length; i++) {
|
|
408
479
|
const a = args[i] as string;
|
|
409
480
|
if (a === '--quiet') { quiet = true; }
|
|
481
|
+
else if (a === '--base') {
|
|
482
|
+
// `--changed` swallows the rest of argv, so `--base` must precede it.
|
|
483
|
+
const v = args[i + 1];
|
|
484
|
+
if (v === undefined || v.startsWith('-')) {
|
|
485
|
+
process.stderr.write(`--base needs a git ref\n${_usage()}`);
|
|
486
|
+
return 2;
|
|
487
|
+
}
|
|
488
|
+
base = v;
|
|
489
|
+
i += 1;
|
|
490
|
+
}
|
|
410
491
|
else if (a === '--changed') { changedFiles = args.slice(i + 1).filter((x) => !x.startsWith('-')); break; }
|
|
411
492
|
else if (a.startsWith('-')) { process.stderr.write(`unrecognized argument: ${a}\n${_usage()}`); return 2; }
|
|
412
493
|
else { process.stderr.write(`unexpected argument: ${a} (did you mean --changed ${a}?)\n${_usage()}`); return 2; }
|
|
413
494
|
}
|
|
495
|
+
if (base !== null && changedFiles === null) {
|
|
496
|
+
// Silently ignoring it would let a workflow think the base-anchored
|
|
497
|
+
// scaffold was in effect when the full audit does not use one at all.
|
|
498
|
+
process.stderr.write(`--base is only meaningful with --changed\n${_usage()}`);
|
|
499
|
+
return 2;
|
|
500
|
+
}
|
|
414
501
|
|
|
415
502
|
// Asserted for BOTH modes, and before either: `--changed` compares each
|
|
416
503
|
// candidate against the same corpus, so a dead root there reports "no
|
|
@@ -434,7 +521,7 @@ export function main(argv?: string[]): number {
|
|
|
434
521
|
}
|
|
435
522
|
|
|
436
523
|
if (changedFiles !== null) {
|
|
437
|
-
const { pairs, fails } = _changed(changedFiles);
|
|
524
|
+
const { pairs, fails } = _changed(changedFiles, base);
|
|
438
525
|
if (pairs.length > 0) {
|
|
439
526
|
const stream = fails > 0 ? process.stderr : process.stdout;
|
|
440
527
|
stream.write(`lint_originality: ${pairs.length} overlap(s) ≥ WARN ${WARN}% (${fails} ≥ FAIL ${FAIL}%):\n`);
|
|
@@ -466,7 +553,7 @@ export function main(argv?: string[]): number {
|
|
|
466
553
|
}
|
|
467
554
|
|
|
468
555
|
function _usage(): string {
|
|
469
|
-
return 'usage: lint_originality [--quiet] [--changed <file...>]\n';
|
|
556
|
+
return 'usage: lint_originality [--quiet] [--base <ref>] [--changed <file...>]\n';
|
|
470
557
|
}
|
|
471
558
|
|
|
472
559
|
function _isCliEntry(): boolean {
|
|
@@ -39,6 +39,7 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
|
39
39
|
|
|
40
40
|
import { parse as parseYaml } from 'yaml';
|
|
41
41
|
|
|
42
|
+
import { workspaceIdentity } from './_lib/git_common_dir.js';
|
|
42
43
|
import { gitEnv } from './_lib/git_env.js';
|
|
43
44
|
import { splitMarkdownRow } from './_lib/md_table.js';
|
|
44
45
|
import { assertScanned, DeadScopeError } from './_lib/scan_scope.js';
|
|
@@ -505,12 +506,26 @@ interface HistoryEntry {
|
|
|
505
506
|
date: string; // %cs — YYYY-MM-DD
|
|
506
507
|
}
|
|
507
508
|
|
|
509
|
+
/**
|
|
510
|
+
* Repo root containing `dir`, or `null` outside a repository.
|
|
511
|
+
*
|
|
512
|
+
* Census rows R5–R7. The three call sites below each spawned
|
|
513
|
+
* `rev-parse --show-toplevel` through `_git` — already `GIT_DIR`-safe via
|
|
514
|
+
* `gitEnv()`, so this migration is behaviour-identical by construction and
|
|
515
|
+
* exists to remove the third, fourth and fifth re-derivation of one question,
|
|
516
|
+
* not to fix a defect. The resolver reads files instead of spawning, so the
|
|
517
|
+
* three checks also stop costing three subprocesses.
|
|
518
|
+
*/
|
|
519
|
+
function _repoRootOf(dir: string): string | null {
|
|
520
|
+
const root = workspaceIdentity(dir).repoRoot;
|
|
521
|
+
return root.resolved ? root.value : null;
|
|
522
|
+
}
|
|
523
|
+
|
|
508
524
|
/** Newest-first commit history of a file; empty for untracked / non-repo paths. */
|
|
509
525
|
export function fileHistory(absPath: string): HistoryEntry[] {
|
|
510
526
|
const dir = path.dirname(absPath);
|
|
511
|
-
const
|
|
512
|
-
if (
|
|
513
|
-
const root = top.trim();
|
|
527
|
+
const root = _repoRootOf(dir);
|
|
528
|
+
if (root === null) return [];
|
|
514
529
|
// Normalize separators: git pathspecs use `/` on every platform, so a raw
|
|
515
530
|
// Windows `path.relative()` result matches nothing and would silently yield
|
|
516
531
|
// an empty history — which the grandfather clause reads as "no baseline".
|
|
@@ -540,9 +555,8 @@ export function fileHistory(absPath: string): HistoryEntry[] {
|
|
|
540
555
|
*/
|
|
541
556
|
export function hasUncommittedChanges(absPath: string): boolean {
|
|
542
557
|
const dir = path.dirname(absPath);
|
|
543
|
-
const
|
|
544
|
-
if (
|
|
545
|
-
const root = top.trim();
|
|
558
|
+
const root = _repoRootOf(dir);
|
|
559
|
+
if (root === null) return false;
|
|
546
560
|
const rel = path.relative(root, absPath).split(path.sep).join('/');
|
|
547
561
|
const out = _git(root, ['status', '--porcelain', '--', rel]);
|
|
548
562
|
if (out === null) return false;
|
|
@@ -551,9 +565,8 @@ export function hasUncommittedChanges(absPath: string): boolean {
|
|
|
551
565
|
|
|
552
566
|
function _blobAt(absPath: string, sha: string): string | null {
|
|
553
567
|
const dir = path.dirname(absPath);
|
|
554
|
-
const
|
|
555
|
-
if (
|
|
556
|
-
const root = top.trim();
|
|
568
|
+
const root = _repoRootOf(dir);
|
|
569
|
+
if (root === null) return null;
|
|
557
570
|
const rel = path.relative(root, absPath).split(path.sep).join('/');
|
|
558
571
|
return _git(root, ['show', `${sha}:${rel}`]);
|
|
559
572
|
}
|