@event4u/agent-config 14.6.0 → 14.8.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 +255 -145
- package/README.md +1 -1
- package/dist/agent-src/commands/analyze/inbox.md +264 -10
- package/dist/agent-src/commands/analyze.md +1 -1
- package/dist/agent-src/commands/evals/git-pr-merge.json +13 -0
- package/dist/agent-src/commands/evals/roadmap.json +17 -1
- package/dist/agent-src/commands/pr/create.md +14 -2
- package/dist/agent-src/commands/pr/merge.md +379 -0
- package/dist/agent-src/commands/roadmap/process-full.md +272 -32
- package/dist/agent-src/commands/roadmap.md +1 -1
- package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +46 -36
- package/dist/agent-src/contexts/execution/autonomy-mechanics.md +37 -2
- package/dist/agent-src/contexts/execution/roadmap-execution-contract.md +95 -2
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +289 -106
- package/dist/agent-src/rules/artifact-drafting-protocol.md +5 -1
- package/dist/agent-src/rules/context-hygiene.md +2 -0
- package/dist/agent-src/rules/decision-revisit-gate.md +67 -32
- package/dist/agent-src/rules/fix-what-you-see.md +108 -0
- package/dist/agent-src/rules/no-roadmap-references.md +0 -5
- package/dist/agent-src/rules/recurring-criticism.md +110 -0
- package/dist/agent-src/rules/roadmap-progress-sync.md +20 -1
- package/dist/agent-src/rules/rule-type-governance.md +0 -2
- package/dist/agent-src/rules/self-repair-loop.md +17 -0
- package/dist/agent-src/rules/skill-quality.md +0 -2
- package/dist/agent-src/rules/source-confidentiality.md +37 -7
- package/dist/agent-src/scripts/archival_sweep.ts +117 -0
- package/dist/agent-src/scripts/archive_completed_roadmaps.ts +26 -7
- package/dist/agent-src/scripts/dashboard_mode.ts +212 -0
- package/dist/agent-src/scripts/gate_budget.ts +282 -0
- package/dist/agent-src/scripts/gate_execute.ts +72 -31
- package/dist/agent-src/scripts/roadmap_gates.ts +74 -3
- package/dist/agent-src/scripts/update_roadmap_progress.ts +296 -53
- package/dist/agent-src/skills/adr-create/SKILL.md +124 -5
- package/dist/agent-src/skills/ai-council/SKILL.md +48 -807
- package/dist/agent-src/skills/ai-council/references/advanced-modes.md +320 -0
- package/dist/agent-src/skills/ai-council/references/cost-and-redaction.md +99 -0
- package/dist/agent-src/skills/ai-council/references/execution-modes.md +162 -0
- package/dist/agent-src/skills/ai-council/references/output-and-synthesis.md +125 -0
- package/dist/agent-src/skills/ai-council/references/procedure.md +143 -0
- package/dist/agent-src/skills/decision-record/SKILL.md +61 -3
- package/dist/agent-src/skills/decision-review/SKILL.md +65 -0
- package/dist/agent-src/skills/merge-conflicts/SKILL.md +26 -0
- package/dist/agent-src/skills/quality-tools/SKILL.md +34 -326
- package/dist/agent-src/skills/quality-tools/references/js-ts-tools.md +113 -0
- package/dist/agent-src/skills/quality-tools/references/php-tools.md +222 -0
- package/dist/agent-src/skills/roadmap-management/SKILL.md +15 -409
- package/dist/agent-src/skills/roadmap-management/references/archival.md +226 -0
- package/dist/agent-src/skills/roadmap-management/references/authoring.md +107 -0
- package/dist/agent-src/skills/roadmap-management/references/dashboard.md +47 -0
- package/dist/agent-src/skills/roadmap-management/references/lifecycle.md +58 -0
- package/dist/agent-src/skills/security/SKILL.md +8 -0
- package/dist/agent-src/skills/security-audit/SKILL.md +12 -1
- package/dist/agent-src/skills/skill-writing/SKILL.md +34 -570
- package/dist/agent-src/skills/skill-writing/references/procedure.md +275 -0
- package/dist/agent-src/skills/skill-writing/references/read-only-scripts.md +18 -0
- package/dist/agent-src/skills/skill-writing/references/section-patterns.md +298 -0
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/roadmaps.md +65 -4
- package/dist/agent-src/templates/scripts/telemetry/flush_sender.mjs +163 -0
- package/dist/agent-src/templates/scripts/telemetry/remote.ts +160 -7
- package/dist/agent-src/templates/scripts/telemetry/transport.ts +169 -0
- package/dist/cli/registry.js +2 -1
- package/dist/cli/registry.js.map +1 -1
- package/dist/cli-delegate/chunk-2GNWGMAL.js +172 -0
- package/dist/cli-delegate/{chunk-P6LQDIWK.js → chunk-7PRE6ZV2.js} +338 -0
- package/dist/cli-delegate/{chunk-BKO2AZ7Z.js → chunk-B2ERU2VA.js} +1 -1
- package/dist/cli-delegate/chunk-E4XIZDIB.js +402 -0
- package/dist/cli-delegate/{chunk-3UCJWMMP.js → chunk-EEJUMCDZ.js} +16 -16
- package/dist/cli-delegate/{chunk-NXFWFAYV.js → chunk-G23AT6GD.js} +1 -0
- package/dist/cli-delegate/{chunk-XURJ4AQC.js → chunk-H4VIVD4C.js} +385 -284
- package/dist/cli-delegate/chunk-I3EIN2YG.js +36 -0
- package/dist/cli-delegate/{chunk-RLN73PR5.js → chunk-LPPWO5CE.js} +12 -8
- package/dist/cli-delegate/chunk-MWRD5YOH.js +100 -0
- package/dist/cli-delegate/{chunk-UEZ7D35A.js → chunk-N7CDDGI3.js} +4 -23
- package/dist/cli-delegate/{chunk-63WSP6YZ.js → chunk-PMEKGDED.js} +74 -167
- package/dist/cli-delegate/{chunk-GRRPEPRN.js → chunk-T7L2QK3K.js} +24 -5
- package/dist/cli-delegate/{chunk-EVTY7ISR.js → chunk-YM4GZDJG.js} +9 -1
- package/dist/cli-delegate/cmd_adr_effective.js +1162 -0
- package/dist/cli-delegate/cmd_conformance.js +466 -716
- package/dist/cli-delegate/cmd_converge.js +3 -3
- package/dist/cli-delegate/cmd_doctor.js +10 -10
- package/dist/cli-delegate/cmd_explain.js +1 -1
- package/dist/cli-delegate/cmd_export.js +10 -9
- package/dist/cli-delegate/cmd_handoff.js +10 -9
- package/dist/cli-delegate/cmd_memory_get.js +2 -1
- package/dist/cli-delegate/cmd_migrate.js +2 -2
- package/dist/cli-delegate/cmd_packs_active.js +1 -1
- package/dist/cli-delegate/cmd_preflight.js +5 -5
- package/dist/cli-delegate/cmd_prune.js +11 -10
- package/dist/cli-delegate/cmd_route_audit.js +6 -5
- package/dist/cli-delegate/cmd_route_explain.js +3 -3
- package/dist/cli-delegate/cmd_session_recycle.js +840 -30
- package/dist/cli-delegate/cmd_settings_get.js +3 -3
- package/dist/cli-delegate/cmd_settings_migrate.js +10 -9
- package/dist/cli-delegate/cmd_settings_set.js +1 -1
- package/dist/cli-delegate/cmd_sync.js +11 -10
- package/dist/cli-delegate/cmd_uninstall.js +13 -11
- package/dist/cli-delegate/cmd_update.js +4 -4
- package/dist/cli-delegate/cmd_upgrade.js +4 -4
- package/dist/cli-delegate/cmd_validate.js +11 -10
- package/dist/cli-delegate/cmd_versions.js +3 -3
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +111 -37
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +5 -5
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +15 -12
- package/dist/discovery/trust-report.md +3 -3
- package/dist/discovery/workspaces.json +11 -8
- package/dist/hooks/dispatch.js +3165 -2192
- package/dist/install/claudePathsPlan.js +187 -0
- package/dist/install/claudePathsPlan.js.map +1 -0
- package/dist/install/globalRuleLayers.js +169 -0
- package/dist/install/globalRuleLayers.js.map +1 -0
- package/dist/install/hostLayerFingerprint.js +136 -0
- package/dist/install/hostLayerFingerprint.js.map +1 -0
- package/dist/install/install.mjs +553 -425
- package/dist/install/partitionEligibility.js +432 -0
- package/dist/install/partitionEligibility.js.map +1 -0
- package/dist/install/ruleLayerPartition.js +157 -0
- package/dist/install/ruleLayerPartition.js.map +1 -0
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +1863 -1234
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/agent_src.js +41 -0
- package/dist/scripts/_lib/agent_src.js.map +1 -1
- package/dist/scripts/_lib/install_layout.js +83 -0
- package/dist/scripts/_lib/install_layout.js.map +1 -0
- package/dist/scripts/_lib/installed_lock.js +444 -0
- package/dist/scripts/_lib/installed_lock.js.map +1 -0
- package/dist/scripts/_lib/prune_empty_dirs.js +85 -0
- package/dist/scripts/_lib/prune_empty_dirs.js.map +1 -0
- package/dist/scripts/condense.js +63 -251
- package/dist/scripts/condense.js.map +1 -1
- package/dist/server/schemas/settings.js +4 -0
- package/dist/server/schemas/settings.js.map +1 -1
- package/docs/contracts/settings-classes.md +12 -6
- package/docs/guidelines/agent-infra/agent-interaction-and-decision-quality.md +8 -3
- package/docs/guidelines/agent-infra/artifact-drafting-protocol-mechanics.md +45 -1
- package/docs/guidelines/agent-infra/context-hygiene-mechanics.md +26 -0
- package/docs/guidelines/agent-infra/false-green.md +47 -1
- package/docs/guidelines/agent-infra/memory-access.md +17 -0
- package/docs/guidelines/agent-infra/recurring-criticism-mechanics.md +96 -0
- package/package.json +1 -1
- package/src/agent-src/scripts/archival_sweep.ts +117 -0
- package/src/agent-src/scripts/archive_completed_roadmaps.ts +26 -7
- package/src/agent-src/scripts/dashboard_mode.ts +212 -0
- package/src/agent-src/scripts/gate_budget.ts +282 -0
- package/src/agent-src/scripts/gate_execute.ts +72 -31
- package/src/agent-src/scripts/roadmap_gates.ts +74 -3
- package/src/agent-src/scripts/update_roadmap_progress.ts +296 -53
- package/src/agent-src/templates/scripts/telemetry/flush_sender.mjs +163 -0
- package/src/agent-src/templates/scripts/telemetry/remote.ts +160 -7
- package/src/agent-src/templates/scripts/telemetry/transport.ts +169 -0
- package/src/config/agent-settings.template.yml +27 -2
- package/src/config/ci-local-parity.yml +39 -0
- package/src/config/estate-count-budget.json +612 -3
- package/src/config/evaluator-budgets.json +3 -3
- package/src/config/gate-coverage.yml +220 -12
- package/src/config/gate-violation-baselines.json +73 -63
- package/src/config/gitignore-block.txt +15 -2
- package/src/config/hook-latency-budget.json +5 -0
- package/src/config/rule-activation-census.json +14 -6
- package/src/config/rule-stub-ceilings.json +34 -7
- package/src/install/claudePathsPlan.ts +196 -0
- package/src/install/globalRuleLayers.ts +197 -0
- package/src/install/hostLayerFingerprint.ts +145 -0
- package/src/install/partitionEligibility.ts +502 -0
- package/src/install/ruleLayerPartition.ts +212 -0
- package/src/scripts/_cli/cmd_adr_effective.ts +484 -0
- package/src/scripts/_cli/cmd_session_recycle.ts +9 -1
- package/src/scripts/_dispatch.bash +17 -1
- package/src/scripts/_lib/ac_heading.ts +30 -0
- package/src/scripts/_lib/adr_frontmatter.test.ts +365 -0
- package/src/scripts/_lib/adr_frontmatter.ts +459 -0
- package/src/scripts/_lib/agent_src.ts +42 -0
- package/src/scripts/_lib/bench_ab_pinned_repo.ts +162 -0
- package/src/scripts/_lib/bench_ab_workspace.ts +10 -1
- package/src/scripts/_lib/envelope_grounding.ts +49 -9
- package/src/scripts/_lib/installed_lock.ts +50 -5
- package/src/scripts/_lib/layer_overlap_notice.ts +11 -2
- package/src/scripts/_lib/md_section.ts +129 -0
- package/src/scripts/_lib/prune_empty_dirs.ts +80 -0
- package/src/scripts/_lib/scope_guard.sh +26 -3
- package/src/scripts/_lib/self_repair.ts +19 -0
- package/src/scripts/_lib/self_repair_class_b.ts +171 -0
- package/src/scripts/adr/evidence_census.ts +899 -0
- package/src/scripts/adr/regenerate_index.ts +12 -32
- package/src/scripts/adr_cite_check.ts +540 -39
- package/src/scripts/ai_council/cli_help.ts +55 -0
- package/src/scripts/audit_adr_coverage.ts +103 -17
- package/src/scripts/before_complete_hook.ts +153 -28
- package/src/scripts/bench_ab_v2_complexity.ts +27 -7
- package/src/scripts/bench_hook_latency.ts +204 -0
- package/src/scripts/build_archive_index.ts +34 -0
- package/src/scripts/check_adr_frontmatter.ts +574 -39
- package/src/scripts/check_hook_bundle_content.ts +219 -0
- package/src/scripts/check_new_adr_evidence.ts +529 -0
- package/src/scripts/check_references.ts +78 -0
- package/src/scripts/check_release_published.ts +32 -11
- package/src/scripts/check_rule_layer_partition.ts +531 -0
- package/src/scripts/check_single_delivery.ts +194 -17
- package/src/scripts/check_standing_rule_delivery.ts +8 -2
- package/src/scripts/ci_settle.ts +170 -0
- package/src/scripts/condense.ts +79 -259
- package/src/scripts/council_cli.ts +3 -3
- package/src/scripts/dispatch_r2_reviewer.ts +6 -14
- package/src/scripts/external_sources_denylist.json +4 -1
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +77 -14
- package/src/scripts/hooks/block_no_verify.ts +61 -10
- package/src/scripts/hooks/block_unauthorized_git.ts +21 -1
- package/src/scripts/hooks/concern_registry.ts +4 -0
- package/src/scripts/hooks/dispatch_hook.ts +154 -187
- package/src/scripts/hooks/dispatch_issues.ts +38 -19
- package/src/scripts/hooks/fallback_yaml.ts +66 -0
- package/src/scripts/hooks/host_semantics.ts +21 -0
- package/src/scripts/hooks/injection_budget.ts +13 -3
- package/src/scripts/hooks/py_json_dumps.ts +103 -0
- package/src/scripts/hooks/rtk_wrap_hook.ts +8 -3
- package/src/scripts/hooks/run_continuation_hook.ts +8 -5
- package/src/scripts/hooks/ship_diff_volume_hook.ts +81 -15
- package/src/scripts/hooks/source_first_gate_hook.ts +464 -0
- package/src/scripts/hooks/state_io.ts +870 -21
- package/src/scripts/hooks/stdin_failure_policy.ts +96 -0
- package/src/scripts/hooks/subagent_ledger_hook.ts +44 -15
- package/src/scripts/hooks/telemetry_flush_hook.ts +139 -0
- package/src/scripts/hooks/telemetry_self_repair.ts +128 -0
- package/src/scripts/hooks/telemetry_usage_hook.ts +22 -4
- package/src/scripts/hooks/turn_end_gate_hook.ts +81 -15
- package/src/scripts/injection_scan_hook.ts +137 -5
- package/src/scripts/install.ts +16 -11
- package/src/scripts/language_mirror_hook.ts +561 -25
- package/src/scripts/lint_framework_leakage_allowlist.json +2 -2
- package/src/scripts/lint_hook_manifest.ts +49 -0
- package/src/scripts/lint_plan_risk_register.ts +4 -3
- package/src/scripts/lint_provenance_vocabulary.ts +472 -4
- package/src/scripts/lint_skill_router_head.ts +4 -4
- package/src/scripts/mcp_server/tools.ts +2 -2
- package/src/scripts/new_roadmap.ts +136 -0
- package/src/scripts/pack_mcp_content.ts +20 -5
- package/src/scripts/release.ts +37 -38
- package/src/scripts/report_layer_overlap.ts +6 -0
- package/src/scripts/routing_doctor.ts +5 -1
- package/src/scripts/self_repair_hook.ts +37 -5
- package/src/scripts/session_register_hook.ts +11 -0
- package/src/scripts/skill_usage_collect.ts +11 -1
- package/src/scripts/skill_usage_report.ts +278 -8
- package/src/scripts/sync_pr_branch.ts +170 -20
- package/src/server/schemas/settings.ts +8 -0
- package/dist/cli-delegate/chunk-EJITJBJX.js +0 -172
- package/dist/cli-delegate/chunk-PZWRPRWP.js +0 -38
- package/dist/cli-delegate/{chunk-PPQO4NGR.js → chunk-6RJA2F5A.js} +3 -3
- package/dist/cli-delegate/{chunk-RYNADUFG.js → chunk-LWTYTX6F.js} +0 -0
- package/dist/cli-delegate/{chunk-FBKL6JWT.js → chunk-MMSGVZKV.js} +3 -3
|
@@ -32,8 +32,12 @@
|
|
|
32
32
|
*
|
|
33
33
|
* Invocation (from project root):
|
|
34
34
|
* node node_modules/.bin/tsx .augment/scripts/update_roadmap_progress.ts # rewrite
|
|
35
|
+
* node node_modules/.bin/tsx .augment/scripts/update_roadmap_progress.ts --archive # rewrite + archive completed
|
|
35
36
|
* node node_modules/.bin/tsx .augment/scripts/update_roadmap_progress.ts --check # CI: exit 1 if stale
|
|
36
37
|
*
|
|
38
|
+
* `--archive` is opt-in and `--check` refuses it; the full contract, the
|
|
39
|
+
* council record and the sweep spawner live in `archival_sweep.ts`.
|
|
40
|
+
*
|
|
37
41
|
* --- Parity notes (ADR-200) ---
|
|
38
42
|
*
|
|
39
43
|
* - Python `round()` is banker's rounding (round-half-to-even). Percentages and
|
|
@@ -59,6 +63,13 @@ import { spawnSync } from 'node:child_process';
|
|
|
59
63
|
import * as fs from 'node:fs';
|
|
60
64
|
import * as path from 'node:path';
|
|
61
65
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
66
|
+
import { run_archival_sweep } from './archival_sweep.js';
|
|
67
|
+
import {
|
|
68
|
+
evaluateDashboardOnDisk,
|
|
69
|
+
parseModeToken,
|
|
70
|
+
reportCheckVerdict,
|
|
71
|
+
type DashboardMode,
|
|
72
|
+
} from './dashboard_mode.js';
|
|
62
73
|
import type * as YamlModule from 'yaml';
|
|
63
74
|
|
|
64
75
|
const _HERE = fileURLToPath(import.meta.url);
|
|
@@ -280,12 +291,21 @@ function is_draft(fm: Record<string, string>): boolean {
|
|
|
280
291
|
return DRAFT_VALUES.has((fm['status'] ?? '').toLowerCase());
|
|
281
292
|
}
|
|
282
293
|
|
|
283
|
-
|
|
284
|
-
|
|
294
|
+
/**
|
|
295
|
+
* The NAME half of the candidate test. Split out because the parked inventory
|
|
296
|
+
* reads `later/` on purpose and `is_roadmap_candidate` rejects any path carrying
|
|
297
|
+
* an excluded component — `later` included. Sharing this half keeps
|
|
298
|
+
* `later/README.md` excluded from both sides by one rule rather than two.
|
|
299
|
+
*/
|
|
300
|
+
function is_roadmap_name(name: string): boolean {
|
|
285
301
|
if (EXCLUDE_NAMES.has(name)) {
|
|
286
302
|
return false;
|
|
287
303
|
}
|
|
288
|
-
|
|
304
|
+
return !EXCLUDE_PREFIXES.some((pre) => name.startsWith(pre));
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function is_roadmap_candidate(p: string): boolean {
|
|
308
|
+
if (!is_roadmap_name(path.basename(p))) {
|
|
289
309
|
return false;
|
|
290
310
|
}
|
|
291
311
|
// path.parts — every component of the path; exclude if any is an excluded dir.
|
|
@@ -741,6 +761,140 @@ function collect(roadmap_root: string): RoadmapStats[] {
|
|
|
741
761
|
return results;
|
|
742
762
|
}
|
|
743
763
|
|
|
764
|
+
/** One parked roadmap, as the inventory needs it. */
|
|
765
|
+
interface ParkedRoadmap {
|
|
766
|
+
rel: string;
|
|
767
|
+
resume: string | null;
|
|
768
|
+
/** Only the linter's looser marker matched — CI passes, the cell cannot quote. */
|
|
769
|
+
unlabelled: boolean;
|
|
770
|
+
/** Open blockers still carried by the parked file. */
|
|
771
|
+
open_blockers: number;
|
|
772
|
+
/** Of those, the ones whose owner is the user. */
|
|
773
|
+
needs_user: number;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
/**
|
|
777
|
+
* A line that STATES when the roadmap comes back. Two tiers exist because
|
|
778
|
+
* `lint_roadmap_later_disposition` accepts a bare `trigger`, which is too loose to
|
|
779
|
+
* quote from — measured over the live tree it matched a `Source:` path containing
|
|
780
|
+
* `mixed-trigger-cleanup`, producing cells that were wrong rather than short.
|
|
781
|
+
*/
|
|
782
|
+
const RESUME_STATEMENT = /\b(blocked until|resume when|resume-when|blocked-until)\b/i;
|
|
783
|
+
|
|
784
|
+
/**
|
|
785
|
+
* The linter's looser vocabulary. A file matching only this DOES carry a resume
|
|
786
|
+
* condition as far as CI is concerned, so reporting it as missing would make the
|
|
787
|
+
* dashboard contradict a gate that already passed.
|
|
788
|
+
*/
|
|
789
|
+
const RESUME_LOOSE = /\btrigger\b/i;
|
|
790
|
+
|
|
791
|
+
/**
|
|
792
|
+
* Parked roadmaps, with the resume condition each one records.
|
|
793
|
+
*
|
|
794
|
+
* A view over existing data, NOT a new governance mechanism: nothing here gates,
|
|
795
|
+
* counts toward a ratchet, or changes what `collect()` scans. It exists because
|
|
796
|
+
* `later/` is excluded from `collect()` and from `/roadmap:process-*` — correct,
|
|
797
|
+
* parked work is not backlog — and that exclusion had no counterweight, so a
|
|
798
|
+
* roadmap moved there left the dashboard entirely.
|
|
799
|
+
*/
|
|
800
|
+
function collect_parked(roadmap_root: string): ParkedRoadmap[] {
|
|
801
|
+
const dir = path.join(roadmap_root, 'later');
|
|
802
|
+
let names: string[];
|
|
803
|
+
try {
|
|
804
|
+
names = fs.readdirSync(dir).sort();
|
|
805
|
+
} catch {
|
|
806
|
+
return [];
|
|
807
|
+
}
|
|
808
|
+
const out: ParkedRoadmap[] = [];
|
|
809
|
+
for (const name of names) {
|
|
810
|
+
const full = path.join(dir, name);
|
|
811
|
+
// The NAME half only: the full predicate rejects every path under `later`.
|
|
812
|
+
if (!_isFile(full) || !is_roadmap_name(name)) {
|
|
813
|
+
continue;
|
|
814
|
+
}
|
|
815
|
+
let text: string;
|
|
816
|
+
try {
|
|
817
|
+
text = fs.readFileSync(full, { encoding: 'utf-8' });
|
|
818
|
+
} catch {
|
|
819
|
+
continue;
|
|
820
|
+
}
|
|
821
|
+
if (is_draft(parse_frontmatter(text))) {
|
|
822
|
+
continue;
|
|
823
|
+
}
|
|
824
|
+
const cell = resume_cell(text);
|
|
825
|
+
// Parked blockers are still open blockers — counting them here is what stops
|
|
826
|
+
// this section becoming the surface where a park reads as a resolution.
|
|
827
|
+
const open = parse_blockers(text).filter((b) => !blocker_is_resolved(b));
|
|
828
|
+
out.push({
|
|
829
|
+
rel: name,
|
|
830
|
+
resume: cell.stated,
|
|
831
|
+
unlabelled: cell.unlabelled,
|
|
832
|
+
open_blockers: open.length,
|
|
833
|
+
needs_user: open.filter((b) => blocker_needs_user(b.owner)).length,
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
return out;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
/**
|
|
840
|
+
* What brings a parked roadmap back, as one table cell. Both fields false means the
|
|
841
|
+
* file records nothing — said outright, because an empty cell reads as "nothing to
|
|
842
|
+
* wait for".
|
|
843
|
+
*
|
|
844
|
+
* The PARAGRAPH is joined, not the matched line: roadmap prose wraps at ~80
|
|
845
|
+
* columns, and quoting one line ended 39 of 52 cells mid-clause. A cell stopping at
|
|
846
|
+
* "the pair for" is not a shorter sentence, it is a different claim.
|
|
847
|
+
*
|
|
848
|
+
* Both tiers are tested over the SAME lines. Testing the loose one whole-file while
|
|
849
|
+
* the strict one ran per line made `unlabelled` fire on any file using the word
|
|
850
|
+
* "trigger" anywhere.
|
|
851
|
+
*/
|
|
852
|
+
function resume_cell(text: string): { stated: string | null; unlabelled: boolean } {
|
|
853
|
+
const lines = text.split('\n');
|
|
854
|
+
for (let i = 0; i < lines.length; i++) {
|
|
855
|
+
const raw = lines[i] as string;
|
|
856
|
+
if (!RESUME_STATEMENT.test(raw)) {
|
|
857
|
+
continue;
|
|
858
|
+
}
|
|
859
|
+
const parts: string[] = [raw];
|
|
860
|
+
// Continuation lines of the same paragraph: non-blank, and not the start of
|
|
861
|
+
// a new block (list item, heading, fence, table row).
|
|
862
|
+
for (let j = i + 1; j < lines.length; j++) {
|
|
863
|
+
const next = (lines[j] as string).replace(/^\s*>\s?/, '').trim();
|
|
864
|
+
if (next === '' || /^([-*+]\s|#{1,6}\s|```|\||>\s*$)/.test(next)) {
|
|
865
|
+
break;
|
|
866
|
+
}
|
|
867
|
+
parts.push(next);
|
|
868
|
+
}
|
|
869
|
+
const joined = cell_text(parts.join(' '));
|
|
870
|
+
if (joined === '') {
|
|
871
|
+
continue;
|
|
872
|
+
}
|
|
873
|
+
return { stated: joined, unlabelled: false };
|
|
874
|
+
}
|
|
875
|
+
return { stated: null, unlabelled: lines.some((l) => RESUME_LOOSE.test(l)) };
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
/**
|
|
879
|
+
* One roadmap sentence, safe between two pipes. ORDER IS LOAD-BEARING: truncate
|
|
880
|
+
* before escaping, because cutting an escaped string severs a `\|` and leaves a
|
|
881
|
+
* dangling backslash. HTML comments are removed, not escaped — a `<!--` reaching a
|
|
882
|
+
* cell comments out the rest of the document, and one already did.
|
|
883
|
+
*/
|
|
884
|
+
function cell_text(raw: string): string {
|
|
885
|
+
let out = raw
|
|
886
|
+
.replace(/^\s*>\s?/, '')
|
|
887
|
+
.replace(/<!--[\s\S]*?-->/g, '')
|
|
888
|
+
.replace(/<!--|-->/g, '')
|
|
889
|
+
.replace(/\*\*/g, '')
|
|
890
|
+
.replace(/\s+/g, ' ')
|
|
891
|
+
.trim();
|
|
892
|
+
if (out.length > 200) {
|
|
893
|
+
out = `${out.slice(0, 197).trimEnd()}...`;
|
|
894
|
+
}
|
|
895
|
+
return out.replace(/\|/g, '\\|');
|
|
896
|
+
}
|
|
897
|
+
|
|
744
898
|
function unarchived_complete(roadmaps: RoadmapStats[]): RoadmapStats[] {
|
|
745
899
|
return roadmaps.filter((r) => r.total_active > 0 && r.open_ === 0 && r.deferred === 0);
|
|
746
900
|
}
|
|
@@ -812,7 +966,7 @@ function collect_bundles(repo_root: string): Bundle[] {
|
|
|
812
966
|
return out;
|
|
813
967
|
}
|
|
814
968
|
|
|
815
|
-
function render(roadmaps: RoadmapStats[], bundles: Bundle[] | null
|
|
969
|
+
function render(roadmaps: RoadmapStats[], bundles: Bundle[] | null, roadmap_root: string): string {
|
|
816
970
|
const total_done = roadmaps.reduce((s, r) => s + r.done, 0);
|
|
817
971
|
const total_active = roadmaps.reduce((s, r) => s + r.total_active, 0);
|
|
818
972
|
const overall_pct = total_active ? _pyRound((total_done * 100) / total_active) : 0;
|
|
@@ -836,6 +990,7 @@ function render(roadmaps: RoadmapStats[], bundles: Bundle[] | null = null): stri
|
|
|
836
990
|
'[skipped/](roadmaps/skipped/) · [later/](roadmaps/later/)' +
|
|
837
991
|
(total_open_blockers > 0
|
|
838
992
|
? ` · **${total_open_blockers}** open blocker${total_open_blockers !== 1 ? 's' : ''}` +
|
|
993
|
+
' in the active tree' +
|
|
839
994
|
(user_open_blockers > 0
|
|
840
995
|
? `, **${user_open_blockers}** need you → \`agent-config gates\``
|
|
841
996
|
: '')
|
|
@@ -849,8 +1004,9 @@ function render(roadmaps: RoadmapStats[], bundles: Bundle[] | null = null): stri
|
|
|
849
1004
|
// script name covers the rest.
|
|
850
1005
|
'> Auto-generated — do not edit. Regenerate with `task roadmap-progress` ' +
|
|
851
1006
|
'or by running the `update_roadmap_progress` script for your install; ' +
|
|
852
|
-
'rewritten on every roadmap create / execute / completion change ' +
|
|
853
|
-
'
|
|
1007
|
+
'rewritten on every roadmap create / execute / completion change. ' +
|
|
1008
|
+
'A repository that does not commit this file has no git history for ' +
|
|
1009
|
+
'it — regenerate to see the current state.\n>\n' +
|
|
854
1010
|
header_meta,
|
|
855
1011
|
);
|
|
856
1012
|
lines.push('## Overall\n');
|
|
@@ -939,6 +1095,58 @@ function render(roadmaps: RoadmapStats[], bundles: Bundle[] | null = null): stri
|
|
|
939
1095
|
});
|
|
940
1096
|
lines.push('');
|
|
941
1097
|
lines.push('---\n');
|
|
1098
|
+
|
|
1099
|
+
// Listed under the active table so the two are read together. Parked work
|
|
1100
|
+
// carries no counts here; what a reader needs is what brings it back.
|
|
1101
|
+
const parked = collect_parked(roadmap_root);
|
|
1102
|
+
if (parked.length > 0) {
|
|
1103
|
+
lines.push(
|
|
1104
|
+
`## Parked — \`later/\` (${parked.length} roadmap` +
|
|
1105
|
+
`${parked.length !== 1 ? 's' : ''}, not active backlog)\n`,
|
|
1106
|
+
);
|
|
1107
|
+
lines.push(
|
|
1108
|
+
'> Excluded from the table above and from `/roadmap:process-*` by ' +
|
|
1109
|
+
'design. Listed here so a resume condition is visible without ' +
|
|
1110
|
+
'opening the file.\n',
|
|
1111
|
+
);
|
|
1112
|
+
const parked_open = parked.reduce((n, r) => n + r.open_blockers, 0);
|
|
1113
|
+
const parked_user = parked.reduce((n, r) => n + r.needs_user, 0);
|
|
1114
|
+
if (parked_open > 0) {
|
|
1115
|
+
lines.push(
|
|
1116
|
+
`> Carrying **${parked_open}** open blocker` +
|
|
1117
|
+
`${parked_open !== 1 ? 's' : ''}` +
|
|
1118
|
+
(parked_user > 0 ? `, **${parked_user}** owned by you` : '') +
|
|
1119
|
+
' — parking resolves nothing, so these are NOT in the ' +
|
|
1120
|
+
'active-tree count above.\n',
|
|
1121
|
+
);
|
|
1122
|
+
}
|
|
1123
|
+
lines.push('| Roadmap | Open blockers | Resume when |');
|
|
1124
|
+
lines.push('|---|---:|---|');
|
|
1125
|
+
for (const r of parked) {
|
|
1126
|
+
const cell =
|
|
1127
|
+
r.resume ??
|
|
1128
|
+
(r.unlabelled
|
|
1129
|
+
? '_condition present but unlabelled — see file_'
|
|
1130
|
+
: '_no resume line recorded_');
|
|
1131
|
+
const blockers =
|
|
1132
|
+
r.open_blockers === 0
|
|
1133
|
+
? '0'
|
|
1134
|
+
: `${r.open_blockers}${r.needs_user > 0 ? ` (${r.needs_user} you)` : ''}`;
|
|
1135
|
+
// `<!-- ref-ignore -->` per row — a correctness fix, not a suppression. A
|
|
1136
|
+
// resume condition NAMES WHAT DOES NOT EXIST YET ("when `x.md` exists" is
|
|
1137
|
+
// the commonest shape in `later/`), so checking these quoted paths as live
|
|
1138
|
+
// references fires the gate on correct content. It fired immediately. The
|
|
1139
|
+
// authoritative copy of each path is the roadmap file; this table quotes
|
|
1140
|
+
// it, and that gate does not walk `later/` either way.
|
|
1141
|
+
lines.push(
|
|
1142
|
+
`| [${r.rel}](roadmaps/later/${r.rel}) | ${blockers} | ${cell} |` +
|
|
1143
|
+
' <!-- ref-ignore -->',
|
|
1144
|
+
);
|
|
1145
|
+
}
|
|
1146
|
+
lines.push('');
|
|
1147
|
+
lines.push('---\n');
|
|
1148
|
+
}
|
|
1149
|
+
|
|
942
1150
|
lines.push('## Per-roadmap phase breakdown\n');
|
|
943
1151
|
for (const r of roadmaps) {
|
|
944
1152
|
lines.push(`### [${r.rel}](roadmaps/${r.rel})\n`);
|
|
@@ -1013,16 +1221,32 @@ class ArgparseExit extends Error {
|
|
|
1013
1221
|
const _PROG = 'update_roadmap_progress.py';
|
|
1014
1222
|
|
|
1015
1223
|
function _usage(): string {
|
|
1016
|
-
return
|
|
1224
|
+
return (
|
|
1225
|
+
`usage: ${_PROG} [-h] [--check] [--tracked-mode | --untracked-mode]\n` +
|
|
1226
|
+
` [--archive | --no-archive] [--repo-root REPO_ROOT] [--dashboard-only]\n`
|
|
1227
|
+
);
|
|
1017
1228
|
}
|
|
1018
1229
|
|
|
1019
1230
|
interface Args {
|
|
1020
1231
|
check: boolean;
|
|
1232
|
+
mode: DashboardMode;
|
|
1233
|
+
archive: boolean;
|
|
1021
1234
|
repo_root: string;
|
|
1235
|
+
/**
|
|
1236
|
+
* Assert ONLY this artefact's own state — tracked / absent / stale — not
|
|
1237
|
+
* the estate-wide conditions `--check` also carries. Two obligations, two
|
|
1238
|
+
* blast radii: why that split exists, and what it cost when it did not,
|
|
1239
|
+
* is on the `roadmap-dashboard-untracked-check` task in
|
|
1240
|
+
* `taskfiles/content.yml`.
|
|
1241
|
+
*/
|
|
1242
|
+
dashboard_only: boolean;
|
|
1022
1243
|
}
|
|
1023
1244
|
|
|
1024
1245
|
function _parseArgs(argv: readonly string[]): Args {
|
|
1025
1246
|
let check = false;
|
|
1247
|
+
let mode: DashboardMode = 'tracked';
|
|
1248
|
+
let archive = false;
|
|
1249
|
+
let dashboard_only = false;
|
|
1026
1250
|
let repo_root = process.cwd();
|
|
1027
1251
|
const emitError = (msg: string): never => {
|
|
1028
1252
|
process.stderr.write(_usage());
|
|
@@ -1038,6 +1262,18 @@ function _parseArgs(argv: readonly string[]): Args {
|
|
|
1038
1262
|
} else if (tok === '--check') {
|
|
1039
1263
|
check = true;
|
|
1040
1264
|
i += 1;
|
|
1265
|
+
} else if (parseModeToken(tok) !== null) {
|
|
1266
|
+
mode = parseModeToken(tok) as DashboardMode;
|
|
1267
|
+
i += 1;
|
|
1268
|
+
} else if (tok === '--dashboard-only') {
|
|
1269
|
+
dashboard_only = true;
|
|
1270
|
+
i += 1;
|
|
1271
|
+
} else if (tok === '--archive') {
|
|
1272
|
+
archive = true;
|
|
1273
|
+
i += 1;
|
|
1274
|
+
} else if (tok === '--no-archive') {
|
|
1275
|
+
archive = false;
|
|
1276
|
+
i += 1;
|
|
1041
1277
|
} else if (tok === '--repo-root') {
|
|
1042
1278
|
const val = argv[i + 1];
|
|
1043
1279
|
if (val === undefined) {
|
|
@@ -1052,7 +1288,16 @@ function _parseArgs(argv: readonly string[]): Args {
|
|
|
1052
1288
|
emitError(`unrecognized arguments: ${tok}`);
|
|
1053
1289
|
}
|
|
1054
1290
|
}
|
|
1055
|
-
|
|
1291
|
+
if (check && archive) {
|
|
1292
|
+
// A gate that mutates the tree it is checking cannot be trusted by CI
|
|
1293
|
+
// (council 2026-08-20, both seats). Refuse the combination outright
|
|
1294
|
+
// rather than silently letting one win.
|
|
1295
|
+
emitError('argument --archive: not allowed with --check');
|
|
1296
|
+
}
|
|
1297
|
+
if (dashboard_only && !check) {
|
|
1298
|
+
emitError('argument --dashboard-only: only meaningful with --check');
|
|
1299
|
+
}
|
|
1300
|
+
return { check, mode, archive, repo_root, dashboard_only };
|
|
1056
1301
|
}
|
|
1057
1302
|
|
|
1058
1303
|
/**
|
|
@@ -1094,15 +1339,35 @@ function main(argv?: readonly string[]): number {
|
|
|
1094
1339
|
process.stdout.write(`ℹ️ No roadmaps directory at ${roadmap_root} — nothing to do.\n`);
|
|
1095
1340
|
return 0;
|
|
1096
1341
|
}
|
|
1342
|
+
// Archive before rendering, so the dashboard describes the tree the sweep
|
|
1343
|
+
// leaves behind rather than the one it found. `--check` never archives: a
|
|
1344
|
+
// gate that mutates the tree it is checking cannot be trusted by CI.
|
|
1345
|
+
let sweep_out = '';
|
|
1346
|
+
if (!args.check && args.archive && unarchived_complete(collect(roadmap_root)).length > 0) {
|
|
1347
|
+
const swept = run_archival_sweep(repo_root);
|
|
1348
|
+
sweep_out = swept.stdout;
|
|
1349
|
+
if (swept.stderr) {
|
|
1350
|
+
process.stderr.write(swept.stderr);
|
|
1351
|
+
}
|
|
1352
|
+
if (!swept.ok) {
|
|
1353
|
+
// A half-finished sweep leaves the tree in a shape the dashboard
|
|
1354
|
+
// would describe as if it were intentional. Report and stop before
|
|
1355
|
+
// writing (council 2026-08-20: "archival failures must prevent
|
|
1356
|
+
// dashboard rendering so partially updated state is not presented
|
|
1357
|
+
// as current").
|
|
1358
|
+
process.stderr.write(
|
|
1359
|
+
'❌ The archival sweep failed — dashboard NOT regenerated. ' +
|
|
1360
|
+
'Fix the tree, then re-run.\n',
|
|
1361
|
+
);
|
|
1362
|
+
return 1;
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1097
1365
|
const roadmaps = collect(roadmap_root);
|
|
1098
|
-
const new_text = render(roadmaps, collect_bundles(repo_root));
|
|
1099
|
-
const current = fs.existsSync(target) ? fs.readFileSync(target, { encoding: 'utf-8' }) : '';
|
|
1366
|
+
const new_text = render(roadmaps, collect_bundles(repo_root), roadmap_root);
|
|
1100
1367
|
const complete = unarchived_complete(roadmaps);
|
|
1101
1368
|
const pending = pending_iron_law_3(roadmaps);
|
|
1102
1369
|
const gated = merge_gated_pending(roadmaps);
|
|
1103
1370
|
|
|
1104
|
-
const _relToRepo = (p: string): string => _relPosix(repo_root, p);
|
|
1105
|
-
|
|
1106
1371
|
const _warn_merge_gated = (): void => {
|
|
1107
1372
|
process.stderr.write(
|
|
1108
1373
|
'⏳ Merge-gated roadmaps (every open item gated on a PR) — ' +
|
|
@@ -1119,54 +1384,32 @@ function main(argv?: readonly string[]): number {
|
|
|
1119
1384
|
};
|
|
1120
1385
|
|
|
1121
1386
|
if (args.check) {
|
|
1122
|
-
const
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
}
|
|
1131
|
-
if (
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
'move them to `agents/roadmaps/archive/` (per the ' +
|
|
1135
|
-
'`roadmap-progress-sync` rule):\n',
|
|
1136
|
-
);
|
|
1137
|
-
for (const r of complete) {
|
|
1138
|
-
process.stderr.write(` - ${r.rel} (${r.done}/${r.total_active} done)\n`);
|
|
1139
|
-
}
|
|
1140
|
-
}
|
|
1141
|
-
if (pending.length) {
|
|
1142
|
-
process.stderr.write(
|
|
1143
|
-
'❌ Iron Law 3 — roadmaps with unresolved `[~]` deferred ' +
|
|
1144
|
-
'items must NOT auto-archive. Resolve via `roadmap-management § 4b` ' +
|
|
1145
|
-
'(spawn follow-up, restore, or cancel):\n',
|
|
1146
|
-
);
|
|
1147
|
-
for (const r of pending) {
|
|
1148
|
-
process.stderr.write(
|
|
1149
|
-
` - ${r.rel} (${r.done}/${r.total_active} done · ` +
|
|
1150
|
-
`${r.deferred} deferred)\n`,
|
|
1151
|
-
);
|
|
1152
|
-
}
|
|
1153
|
-
}
|
|
1154
|
-
if (gated.length) {
|
|
1155
|
-
_warn_merge_gated();
|
|
1156
|
-
}
|
|
1157
|
-
if (stale || complete.length || pending.length) {
|
|
1158
|
-
return 1;
|
|
1159
|
-
}
|
|
1160
|
-
process.stdout.write(`✅ ${_relToRepo(target)} is up to date.\n`);
|
|
1161
|
-
return 0;
|
|
1387
|
+
const rel = _relPosix(repo_root, target);
|
|
1388
|
+
const r = reportCheckVerdict({
|
|
1389
|
+
verdict: evaluateDashboardOnDisk({ mode: args.mode, target, repo_root, rendered: new_text, rel }),
|
|
1390
|
+
complete,
|
|
1391
|
+
pending,
|
|
1392
|
+
dashboardOnly: args.dashboard_only,
|
|
1393
|
+
warnMergeGated: _warn_merge_gated,
|
|
1394
|
+
gatedCount: gated.length,
|
|
1395
|
+
});
|
|
1396
|
+
if (r.stderr) process.stderr.write(r.stderr);
|
|
1397
|
+
if (r.stdout) process.stdout.write(r.stdout);
|
|
1398
|
+
return r.rc;
|
|
1162
1399
|
}
|
|
1163
1400
|
fs.writeFileSync(target, new_text, { encoding: 'utf-8' });
|
|
1164
1401
|
process.stdout.write(
|
|
1165
|
-
`✅ Wrote ${
|
|
1402
|
+
`✅ Wrote ${_relPosix(repo_root, target)} · ` +
|
|
1166
1403
|
`${roadmaps.length} roadmap(s) · ` +
|
|
1167
1404
|
`${roadmaps.reduce((s, r) => s + r.done, 0)}/${roadmaps.reduce((s, r) => s + r.total_active, 0)} steps done.\n`,
|
|
1168
1405
|
);
|
|
1406
|
+
if (sweep_out) {
|
|
1407
|
+
process.stdout.write(sweep_out);
|
|
1408
|
+
}
|
|
1169
1409
|
if (complete.length) {
|
|
1410
|
+
// Whatever survived the sweep is complete but NOT archivable — the
|
|
1411
|
+
// sweep already said why on stderr (an open blocker outlives its
|
|
1412
|
+
// steps). Without `--archive` this is every complete roadmap.
|
|
1170
1413
|
process.stderr.write(
|
|
1171
1414
|
'⚠️ Completed roadmaps not yet archived — move to ' +
|
|
1172
1415
|
'`agents/roadmaps/archive/`:\n',
|
|
@@ -45,6 +45,33 @@ Do NOT use when:
|
|
|
45
45
|
supersede it instead of duplicating.
|
|
46
46
|
- A skill, rule, or guideline is the better home (use those skills).
|
|
47
47
|
|
|
48
|
+
## Admission gate — classify before you create
|
|
49
|
+
|
|
50
|
+
Run this **before** picking a surface or a number. An ADR is warranted only
|
|
51
|
+
when the decision is architecturally significant on at least one axis:
|
|
52
|
+
|
|
53
|
+
1. **Hard or costly to reverse** — a one-way door: public API shape, DB
|
|
54
|
+
schema, wire format, published identifier, migration.
|
|
55
|
+
2. **Broadly constraining** — it binds work outside the module that made it.
|
|
56
|
+
3. **Crosses a governed surface** — consumer contract, API, security or
|
|
57
|
+
privacy floor, package structure.
|
|
58
|
+
|
|
59
|
+
None of the three holds → it is **not** an ADR. Route it to its real home: a
|
|
60
|
+
decision note in `agents/decisions/`, a config value, a measurement record in
|
|
61
|
+
`docs/CLAIMS.md`, an experiment, or a roadmap item.
|
|
62
|
+
|
|
63
|
+
Explicitly **not** ADRs: a temporary numeric threshold · a benchmark value ·
|
|
64
|
+
a model mapping · one-off release sequencing · a reversible local
|
|
65
|
+
implementation detail.
|
|
66
|
+
|
|
67
|
+
**The tree's own reference case.** ADR-002 encodes `25 000 → 26 000` and a
|
|
68
|
+
`4.0k` override ceiling as architecture law (`ADR-002:55`, `:62`), and
|
|
69
|
+
ADR-114 then had to add another override while recording that 7 of 9 kernel
|
|
70
|
+
rules already carry them (`ADR-114:74`). The *principle* — a kernel budget
|
|
71
|
+
exists, is measured, and is capped — is the ADR. The numbers belong in a
|
|
72
|
+
versioned budget contract with a regression gate, so a recalibration stops
|
|
73
|
+
needing an architecture supersession.
|
|
74
|
+
|
|
48
75
|
## Goal
|
|
49
76
|
|
|
50
77
|
- Sequential `ADR-NNN-<slug>.md` numbering with no gaps.
|
|
@@ -114,6 +141,11 @@ Use the surface-specific template. All sections are required; "—"
|
|
|
114
141
|
is acceptable for genuinely empty Alternatives or References blocks
|
|
115
142
|
but never for Status, Context, Decision, or Consequences.
|
|
116
143
|
|
|
144
|
+
`## Evidence` and `## Assumptions` are required headings too, with one
|
|
145
|
+
carve-out each: `## Evidence` may be empty only when the record grades itself
|
|
146
|
+
`E0` and says so in the section, and `## Assumptions` may be "—" only when
|
|
147
|
+
every load-bearing claim in the rationale carries a basis ref.
|
|
148
|
+
|
|
117
149
|
**`review_trigger` is required and it names a CONDITION, not a date.** A
|
|
118
150
|
decision is a call made under conditions that held at the time; the trigger
|
|
119
151
|
records which change would make it worth re-deciding. "Review annually" is
|
|
@@ -124,11 +156,52 @@ the event: *"when a second consumer reports the same preservation surprise"*,
|
|
|
124
156
|
below the pre-registered threshold"*. Enforced from 2026-07-25 forward; earlier
|
|
125
157
|
ADRs are grandfathered by date.
|
|
126
158
|
|
|
159
|
+
**Staged — and `terminal` is not one of the stages.** A new or materially
|
|
160
|
+
amended accepted ADR needs a substantive trigger now. An *existing* accepted
|
|
161
|
+
record may carry `review_trigger: unclassified` while the migration runs, and
|
|
162
|
+
that exception count only ever decreases. `terminal`, `none`, an empty value
|
|
163
|
+
and permanence phrasing ("forever", "never revisit", "settled forever") are
|
|
164
|
+
invalid at **every** stage — `check_adr_frontmatter.ts` rejects them, because
|
|
165
|
+
"no trigger — terminal decision" is permanence with softer wording. `terminal`
|
|
166
|
+
is not a migration state; it is the thing the staging exists to stop becoming
|
|
167
|
+
permanent. Superseded, rejected and deprecated records are historical and need
|
|
168
|
+
no active trigger.
|
|
169
|
+
|
|
127
170
|
When you later reopen one, say which **premise moved** and what evidences the
|
|
128
171
|
move — not "we were wrong". If the original was right under its own conditions,
|
|
129
172
|
record that too. A premise that turns out false while the decision stays correct
|
|
130
173
|
gets a logged correction block, never a silent edit.
|
|
131
174
|
|
|
175
|
+
**Three descriptive axes ship on a new record** — `provenance`, `evidence`,
|
|
176
|
+
`authority_basis`. Vocabulary, defaults and the mutation policy are owned by
|
|
177
|
+
[`adr-layout § Provenance and evidence`](../../../docs/contracts/adr-layout.md);
|
|
178
|
+
`check_adr_frontmatter.ts` validates the shape. What the author has to get
|
|
179
|
+
right while drafting:
|
|
180
|
+
|
|
181
|
+
- Every load-bearing factual claim in the rationale either **points at a basis
|
|
182
|
+
ref** — `file:line`, a URL, a `docs/CLAIMS.md` claim id, a benchmark id — or
|
|
183
|
+
is **labelled an assumption** under `## Assumptions`. There is no third
|
|
184
|
+
state, and an unlabelled guess reads as a finding to the next reader.
|
|
185
|
+
- **An empty `## Evidence` section means `E0` by construction, and the record
|
|
186
|
+
says so.** An honest E0 is publishable, exactly as an honest null is; a
|
|
187
|
+
confident grade over an empty section is not.
|
|
188
|
+
- `discovery: incomplete` is the honest default on E0 and stays that value
|
|
189
|
+
until a defined evidence search has run and found nothing. `complete`
|
|
190
|
+
asserts absence — a claim the author owns.
|
|
191
|
+
- **A grade of E2 or above must name a `basis`.** The validator rejects an
|
|
192
|
+
E2/E3/E4 with an empty basis list; grade it lower rather than asserting a
|
|
193
|
+
source you cannot cite.
|
|
194
|
+
- **A council attribution never lifts a grade above E0.** N models agreeing is
|
|
195
|
+
`provenance: agentic` with `agentic_mode: council` — sources and
|
|
196
|
+
measurements raise `strength`, consensus does not. A council is deliberately
|
|
197
|
+
not its own `kind`.
|
|
198
|
+
- A human product decision records `strength: E0` with
|
|
199
|
+
`authority_basis: owner_intent` and does **not** fake a grade. Its authority
|
|
200
|
+
comes from owning the purpose.
|
|
201
|
+
- **A grade prices review burden, never authority.** Nothing about writing
|
|
202
|
+
`E0` makes the record cheaper to overturn *by whom* — see
|
|
203
|
+
[`adr-layout § The reopen record`](../../../docs/contracts/adr-layout.md).
|
|
204
|
+
|
|
132
205
|
**Flat-surface template** (`docs/decisions/ADR-NNN-<slug>.md`):
|
|
133
206
|
|
|
134
207
|
```markdown
|
|
@@ -145,6 +218,17 @@ phase: <roadmap> · <phase-id>
|
|
|
145
218
|
review_trigger: <the CONDITION that would reopen this decision>
|
|
146
219
|
protected_dimensions: [...] # optional — purpose | security_floor | privacy_floor | external_commitment | governance | none
|
|
147
220
|
reopen_policy: directional | owner | unclassified # optional; absent → unclassified
|
|
221
|
+
|
|
222
|
+
provenance:
|
|
223
|
+
kind: human | agentic | mixed | unknown
|
|
224
|
+
decision_makers: [...] # who actually selected it
|
|
225
|
+
human_directed: true | false | unknown
|
|
226
|
+
agentic_mode: single | council | delegated # optional, descriptive only
|
|
227
|
+
evidence:
|
|
228
|
+
strength: E0 | E1 | E2 | E3 | E4
|
|
229
|
+
discovery: complete | incomplete # required when strength is E0
|
|
230
|
+
basis: [...] # file:line | URL | CLAIMS id | benchmark id
|
|
231
|
+
authority_basis: evidence | owner_intent # optional; absent → evidence
|
|
148
232
|
---
|
|
149
233
|
|
|
150
234
|
# ADR-NNN — <Decision Title>
|
|
@@ -153,24 +237,59 @@ reopen_policy: directional | owner | unclassified # optional; absent → uncla
|
|
|
153
237
|
|
|
154
238
|
**<Proposed | Accepted | …>** · YYYY-MM-DD.
|
|
155
239
|
|
|
156
|
-
## Context / Decision / Consequences / Alternatives / References
|
|
240
|
+
## Context / Decision / Evidence / Assumptions / Consequences / Alternatives / References
|
|
157
241
|
```
|
|
158
242
|
|
|
243
|
+
`## Evidence` carries one line per basis ref backing a load-bearing claim.
|
|
244
|
+
`## Assumptions` carries every load-bearing claim that has no basis ref — that
|
|
245
|
+
is what makes the grade honest rather than decorative.
|
|
246
|
+
|
|
159
247
|
**Per-area template** (`docs/adrs/<area>/NNNN-<slug>.md`):
|
|
160
248
|
|
|
161
249
|
```markdown
|
|
250
|
+
---
|
|
251
|
+
adr: NNNN
|
|
252
|
+
area: <area>
|
|
253
|
+
status: proposed | accepted | superseded | deprecated
|
|
254
|
+
date: YYYY-MM-DD
|
|
255
|
+
decision: <slug>
|
|
256
|
+
supersedes: —
|
|
257
|
+
superseded_by: —
|
|
258
|
+
type: retrospective | prospective
|
|
259
|
+
review_trigger: <the CONDITION that would reopen this decision>
|
|
260
|
+
|
|
261
|
+
provenance:
|
|
262
|
+
kind: human | agentic | mixed | unknown
|
|
263
|
+
decision_makers: [...]
|
|
264
|
+
human_directed: true | false | unknown
|
|
265
|
+
agentic_mode: single | council | delegated # optional, descriptive only
|
|
266
|
+
evidence:
|
|
267
|
+
strength: E0 | E1 | E2 | E3 | E4
|
|
268
|
+
discovery: complete | incomplete # required when strength is E0
|
|
269
|
+
basis: [...] # file:line | URL | CLAIMS id | benchmark id
|
|
270
|
+
authority_basis: evidence | owner_intent # optional; absent → evidence
|
|
271
|
+
---
|
|
272
|
+
|
|
162
273
|
# ADR NNNN — <Decision Title>
|
|
163
274
|
|
|
164
275
|
> Area: `<area>` · Status: accepted · Date: YYYY-MM-DD · Type: retrospective | new
|
|
165
276
|
> Roadmap: `agents/roadmaps/<file>.md` <phase-id>
|
|
166
277
|
> Supersedes: —
|
|
167
278
|
|
|
168
|
-
## Context / Decision / Considered alternatives / Consequences / References
|
|
279
|
+
## Context / Decision / Evidence / Assumptions / Considered alternatives / Consequences / References
|
|
169
280
|
```
|
|
170
281
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
282
|
+
The frontmatter block is the same shape as the flat surface — the contract
|
|
283
|
+
says so ([`adr-layout § Frontmatter`](../../../docs/contracts/adr-layout.md):
|
|
284
|
+
"identical across both surfaces"), and `audit_adr_coverage.ts`'s parser reads
|
|
285
|
+
it when present. The quote-style header stays as the human-readable banner,
|
|
286
|
+
and every existing per-area record now carries frontmatter beside it, so their
|
|
287
|
+
generated README tables render real values — see
|
|
288
|
+
[`docs/adrs/telegraph/README.md`](../../../docs/adrs/telegraph/README.md),
|
|
289
|
+
whose rows read `accepted` / `2026-05-16`. A record carrying the banner alone
|
|
290
|
+
is not blank either: `parse_blockquote_meta` reads the
|
|
291
|
+
`> Area: … · Status: … · Date: …` line as a fallback, and only a record with
|
|
292
|
+
neither renders "—". Cite the area's contract from the README in
|
|
174
293
|
[`docs/adrs/<area>/README.md`](../../../docs/adrs/).
|
|
175
294
|
|
|
176
295
|
### 5. Regenerate the index
|