@event4u/agent-config 14.13.0 → 14.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.augment-plugin/marketplace.json +3 -3
- package/.augment-plugin/plugin.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +138 -376
- package/README.md +1 -1
- package/dist/agent-src/commands/analyze/inbox.md +36 -0
- package/dist/agent-src/commands/pr/create.md +16 -1
- package/dist/agent-src/contexts/execution/terminal-states.md +25 -4
- package/dist/agent-src/rules/code-comment-discipline.md +6 -0
- package/dist/agent-src/rules/missing-skill-recovery.md +2 -2
- package/dist/agent-src/rules/token-budget-discipline.md +2 -2
- package/dist/agent-src/scripts/archive_completed_roadmaps.ts +6 -6
- package/dist/agent-src/scripts/guarded_baseline.ts +22 -7
- package/dist/agent-src/scripts/stubs_due.ts +118 -2
- package/dist/agent-src/scripts/update_roadmap_progress.ts +8 -8
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/cli/registry.js +1 -0
- package/dist/cli-delegate/{chunk-ILMUPLGW.js → chunk-7RXIKF62.js} +159 -86
- package/dist/cli-delegate/cmd_conformance.js +39 -2
- package/dist/cli-delegate/cmd_doctor.js +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +7 -7
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +1 -1
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +2 -2
- package/dist/discovery/trust-report.md +1 -1
- package/dist/discovery/workspaces.json +2 -2
- package/dist/hooks/dispatch.js +1696 -812
- package/dist/mcp/registry-manifest.json +2 -2
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +297 -115
- package/docs/guidelines/agent-infra/guarded-baseline.md +1 -1
- package/docs/guidelines/code-clarity.md +18 -0
- package/package.json +1 -1
- package/src/agent-src/scripts/archive_completed_roadmaps.ts +6 -6
- package/src/agent-src/scripts/guarded_baseline.ts +22 -7
- package/src/agent-src/scripts/stubs_due.ts +118 -2
- package/src/agent-src/scripts/update_roadmap_progress.ts +8 -8
- package/src/config/assurance-capability-registry.json +1 -1
- package/src/config/estate-count-budget.json +20 -1
- package/src/config/evaluator-budgets.json +4 -4
- package/src/config/gate-coverage.yml +211 -1
- package/src/config/gate-reachability-exemptions.json +1 -0
- package/src/config/gate-violation-baselines.json +9 -3
- package/src/config/preamble-payload-budget.json +19 -11
- package/src/config/rule-stub-ceilings.json +14 -7
- package/src/config/web-launch-readiness.json +4 -2
- package/src/scripts/_cli/_doctor_emit.ts +71 -0
- package/src/scripts/_cli/_doctor_strict.ts +163 -0
- package/src/scripts/_cli/cmd_doctor.ts +16 -37
- package/src/scripts/_dispatch.bash +11 -4
- package/src/scripts/_lib/activation_ladder.ts +47 -3
- package/src/scripts/_lib/activation_receipt_producer.ts +341 -0
- package/src/scripts/_lib/adr_frontmatter.ts +26 -0
- package/src/scripts/_lib/branch_convergence.ts +186 -0
- package/src/scripts/_lib/candidate_pair_delta.ts +349 -0
- package/src/scripts/_lib/carrier_status.ts +118 -0
- package/src/scripts/_lib/cascade_stage_enumeration.ts +92 -0
- package/src/scripts/_lib/context_observation.ts +132 -0
- package/src/scripts/_lib/continuation_ladder.ts +188 -0
- package/src/scripts/_lib/corpus_manifest.ts +726 -0
- package/src/scripts/_lib/delivery_arm_experiment.ts +18 -2
- package/src/scripts/_lib/estate_carrier_cases.ts +72 -0
- package/src/scripts/_lib/evaluation_cascade.ts +452 -0
- package/src/scripts/_lib/evolution_roi.ts +562 -0
- package/src/scripts/_lib/experiment_binding.ts +95 -0
- package/src/scripts/_lib/harness_evolution_guards.ts +10 -0
- package/src/scripts/_lib/ignored_blocker.ts +8 -1
- package/src/scripts/_lib/lexical_shortlist.ts +215 -0
- package/src/scripts/_lib/llm_candidate_proposer.ts +461 -0
- package/src/scripts/_lib/llm_proposer_transport.ts +153 -0
- package/src/scripts/_lib/outcome_envelope.ts +6 -1
- package/src/scripts/_lib/outcome_vocabularies.ts +135 -5
- package/src/scripts/_lib/pathology_archive.ts +393 -0
- package/src/scripts/_lib/promotion_capability.ts +315 -0
- package/src/scripts/_lib/promotion_evidence.ts +414 -0
- package/src/scripts/_lib/promotion_review.ts +294 -0
- package/src/scripts/_lib/prompt_shape.ts +92 -0
- package/src/scripts/_lib/release_highlights.ts +79 -1
- package/src/scripts/_lib/release_material.ts +125 -2
- package/src/scripts/_lib/repeated_failure.ts +19 -2
- package/src/scripts/_lib/routing_corpus.ts +307 -0
- package/src/scripts/_lib/routing_index_input.ts +104 -0
- package/src/scripts/_lib/rule_injection.ts +22 -1
- package/src/scripts/_lib/runtime_journal.ts +11 -4
- package/src/scripts/_lib/semantic_noop.ts +167 -0
- package/src/scripts/activation_receipt.ts +258 -0
- package/src/scripts/ai_council/cross_exam.ts +128 -0
- package/src/scripts/ai_council/explain_route.ts +157 -0
- package/src/scripts/ai_council/information_gain.ts +293 -0
- package/src/scripts/ai_council/leakage_corpus.ts +385 -0
- package/src/scripts/ai_council/leakage_patterns.ts +453 -0
- package/src/scripts/ai_council/minority_retention.ts +171 -0
- package/src/scripts/ai_council/recouncil_savings.ts +299 -0
- package/src/scripts/ai_council/replay_route.ts +223 -0
- package/src/scripts/ai_council/reviewer_assignment.ts +279 -0
- package/src/scripts/ai_council/routing_training_row.ts +197 -0
- package/src/scripts/ai_council/synthesis_strategy.ts +159 -0
- package/src/scripts/check_adr_frontmatter.ts +4 -9
- package/src/scripts/check_agent_artifact_location.ts +337 -0
- package/src/scripts/check_artefact_count_messaging.ts +74 -8
- package/src/scripts/check_enforcement_coverage.ts +40 -1
- package/src/scripts/check_estate_count.ts +205 -24
- package/src/scripts/check_release_highlights.ts +113 -14
- package/src/scripts/check_roadmap_trackable.ts +17 -2
- package/src/scripts/check_web_launch_readiness.ts +310 -20
- package/src/scripts/corpus_manifest.ts +235 -0
- package/src/scripts/evolution_lab.ts +234 -9
- package/src/scripts/generate_capability_matrix.ts +16 -9
- package/src/scripts/grade_target_readiness.ts +221 -2
- package/src/scripts/hook_effect_doctor.ts +47 -4
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +28 -6
- package/src/scripts/hooks/comment_discipline_hook.ts +146 -0
- package/src/scripts/hooks/concern_registry.ts +2 -0
- package/src/scripts/hooks/run_continuation_hook.ts +78 -88
- package/src/scripts/hooks/session_eol_hook.ts +12 -1
- package/src/scripts/hooks/turn_end_gate_hook.ts +9 -1
- package/src/scripts/install-hooks.sh +18 -0
- package/src/scripts/language_mirror_hook.ts +14 -2
- package/src/scripts/lint_carrier_integrity.ts +820 -0
- package/src/scripts/lint_code_comments.ts +748 -0
- package/src/scripts/lint_plan_risk_register.ts +39 -19
- package/src/scripts/lint_promotion_paths.ts +779 -0
- package/src/scripts/llm_propose.ts +256 -0
- package/src/scripts/measure_delivery_sets.ts +361 -0
- package/src/scripts/measure_routing_signal.ts +307 -0
- package/src/scripts/model_rule_injection.ts +19 -3
- package/src/scripts/probe_council_retention.ts +495 -0
- package/src/scripts/release.ts +21 -37
- package/src/scripts/release_drill.ts +199 -0
- package/src/scripts/release_env.ts +35 -0
- package/src/scripts/release_publication.ts +142 -0
- package/src/scripts/roadmap_context.ts +15 -0
- package/src/scripts/skill_scout.ts +633 -0
- package/src/scripts/sync_pr_branch.ts +521 -42
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ It is both deep **and** disciplined — and honest about what it deliberately is
|
|
|
27
27
|
- **Surgical uninstall** — removes only its own keys from a shared host config (matched by JSON-pointer + SHA-256), never a neighbour tool's entries.<!-- claim:surgical-uninstall -->
|
|
28
28
|
- **Pack-scoped install** — writes the active pack only, not a 500-artefact dump.
|
|
29
29
|
|
|
30
|
-
**What it deliberately is *not*** — the core is a governance layer with optional, individually opt-in embedded engines (code intelligence, gated reach, the setup GUI, the bench lab — [ADR-124](docs/decisions/ADR-124-embedded-engine-doctrine.md)): **no
|
|
30
|
+
**What it deliberately is *not*** — the core is a governance layer with optional, individually opt-in embedded engines (code intelligence, gated reach, the setup GUI, the bench lab — [ADR-124](docs/decisions/ADR-124-embedded-engine-doctrine.md)): **no mandatory or always-on daemon, no separate state database, no self-rewriting memory, no auto-build pipeline.** Engines are never mandatory, never default-on without measured lift, and terminate with the command that invoked them. The host agent runs the loop; every learned change is human-reviewed; the same layer stays portable across tools. Capability without a process to babysit.
|
|
31
31
|
|
|
32
32
|
> **Where this comes from (honest provenance).** The skills, rules and personas
|
|
33
33
|
> are distilled from real production work on TypeScript and PHP codebases. The
|
|
@@ -487,6 +487,7 @@ The question is never "what does the file say" but **"what does it become here"*
|
|
|
487
487
|
| reference material read on demand | a `guideline` or `context` |
|
|
488
488
|
| a measured finding | a `decision-record`/ADR, not a rule |
|
|
489
489
|
| a defect claim | a roadmap item, once verified |
|
|
490
|
+
| a claim that maps onto an existing stub | **not a fresh artifact — a stub-blocked finding, see below** |
|
|
490
491
|
| a consolidation omitting a parent | **not an artifact — a discharge, see below** |
|
|
491
492
|
|
|
492
493
|
**An omitted parent is discharged, never left silent.** When Phase 2's `lineage`
|
|
@@ -511,6 +512,41 @@ A `ghost:` reading — a declared parent with no matching file — has the same
|
|
|
511
512
|
discharges plus a fourth that is really a correction: fix the name. A lineage
|
|
512
513
|
naming a plan nobody can open is the cheaper half of the same defect.
|
|
513
514
|
|
|
515
|
+
**A survivor that maps onto an existing stub is never resolved into a verdict
|
|
516
|
+
line.** When a verified claim lands on something `agents/roadmaps/stubs/` already
|
|
517
|
+
holds, the mapping is neither "a defect claim → a roadmap item" nor "nothing to
|
|
518
|
+
do": the plan exists, and something is holding it. Before this row the table had
|
|
519
|
+
no entry for that state, so a run that found one had no prescribed output and
|
|
520
|
+
wrote a summary sentence instead. The round that produced this row closed exactly
|
|
521
|
+
so — *"Not a neglected guard: the cost of an unmade owner decision. On the owner's
|
|
522
|
+
desk."* That sentence is accurate, and it is why nine feedback rounds on one guard
|
|
523
|
+
produced no executable item: it names a state and hands nobody anything to do.
|
|
524
|
+
|
|
525
|
+
The run's output MUST carry all four of these, per stub matched. Three of them
|
|
526
|
+
are the difference between a finding and a shrug, and the fourth is already
|
|
527
|
+
computed upstream:
|
|
528
|
+
|
|
529
|
+
1. **The stub path** — `agents/roadmaps/stubs/<slug>.md`, so a reader opens it
|
|
530
|
+
instead of searching for it.
|
|
531
|
+
2. **Its blocker slug** — the `### blocker: <slug>` holding it, named. Where the
|
|
532
|
+
hold is recorded outside any blocker section, say so in those words and name
|
|
533
|
+
the section; "waiting on a decision" with nothing named is the verdict line
|
|
534
|
+
again, one word longer.
|
|
535
|
+
3. **Its age in days** — days since the file first appeared under
|
|
536
|
+
`agents/roadmaps/stubs/`, from
|
|
537
|
+
`git log --diff-filter=A --format=%ad --date=short -- <path> | tail -1`. A
|
|
538
|
+
stub parked eleven days and one parked two hundred are different findings and
|
|
539
|
+
a reader cannot tell them apart from the path.
|
|
540
|
+
4. **The recurrence count from Phase 4c** — how many earlier rounds raised the
|
|
541
|
+
same subject. A first arrival and a ninth are different findings, 4c has
|
|
542
|
+
already counted it, and dropping it here is what let one subject arrive nine
|
|
543
|
+
times as if each were new.
|
|
544
|
+
|
|
545
|
+
Missing any of the four leaves the survivor undischarged. This row does not
|
|
546
|
+
decide what happens to the stub — promotion is the estate's question and this
|
|
547
|
+
command does not answer it — it only forbids resolving the survivor into prose
|
|
548
|
+
that names no artifact.
|
|
549
|
+
|
|
514
550
|
Three hard defaults, from this repo's own scar tissue:
|
|
515
551
|
|
|
516
552
|
- **Extend before you create.** Run the four-surface overlap scan from
|
|
@@ -149,10 +149,25 @@ than less: the gate can now read the PR's real `baseRefName` from the forge, so
|
|
|
149
149
|
a stacked or release-line PR is measured against the branch it will actually
|
|
150
150
|
merge into instead of against the repo default.
|
|
151
151
|
|
|
152
|
+
**The whole SEQUENCE is executable now, not just this step.** All six steps —
|
|
153
|
+
fetch, integrate the base set, regenerate, verify, re-check freshness, then hand
|
|
154
|
+
the push back to you — sit behind one runner, and a dry mode prints each step
|
|
155
|
+
with its outcome without touching the tree. In this package that runner is the
|
|
156
|
+
`push-ready` target in `taskfiles/dev.yml`; a consumer project wires the same
|
|
157
|
+
six steps behind whatever its own task runner is, with steps 3 and 4 being its
|
|
158
|
+
own generator and verify commands. The pre-push hook refuses and points at the
|
|
159
|
+
runner; the hook never merges, because a merge inside `pre-push` rewrites the
|
|
160
|
+
tree at the moment you believe your work is finished.
|
|
161
|
+
|
|
152
162
|
**The resolution is executable now, not just described.**
|
|
153
163
|
`./scripts-run src/scripts/sync_pr_branch` resolves the base from the open PR
|
|
154
164
|
(so a stacked or release-line PR is measured against what it actually merges
|
|
155
|
-
into), fetches, and merges it in when the branch is behind.
|
|
165
|
+
into), fetches, and merges it in when the branch is behind. The base is a
|
|
166
|
+
**set**: when the PR targets something other than the default branch, whether
|
|
167
|
+
the default branch joins the set is a per-target policy read from the *target's
|
|
168
|
+
own commit*, never from this branch — see `src/scripts/_lib/branch_convergence.ts`.
|
|
169
|
+
A non-default target with no policy entry is refused, not guessed. The default
|
|
170
|
+
is merged first, so a conflict surfaces against the broader base first. On a conflict it
|
|
156
171
|
STOPS and splits the conflicted paths into generated, remeasured and authored —
|
|
157
172
|
the first has one correct resolution (regenerate), the second's is to re-run the
|
|
158
173
|
measurement on the merged tree (a ratchet baseline, where picking a side is how
|
|
@@ -10,7 +10,7 @@ identically to a win: a loop that hit its iteration cap, a gate that could not
|
|
|
10
10
|
run, an approval that never came — each ending with a summary that says the work
|
|
11
11
|
is done.
|
|
12
12
|
|
|
13
|
-
## The
|
|
13
|
+
## The seven states
|
|
14
14
|
|
|
15
15
|
| state | what it means | what it is NOT |
|
|
16
16
|
|---|---|---|
|
|
@@ -20,6 +20,23 @@ is done.
|
|
|
20
20
|
| `approval-required` | The work is ready and the next action crosses a gate the agent may not cross alone. | Not `blocked`: nothing is missing, and the run produced a deliverable. |
|
|
21
21
|
| `exhausted` | A declared budget ran out — iterations, tokens, spend, wall-clock. | **Never** `success`, whatever partial progress exists. The cap firing IS the outcome. |
|
|
22
22
|
| `stagnated` | The budget has not run out and progress has stopped: the same failure signature is repeating. | Not `exhausted`. Stagnation is detected by a NO-PROGRESS signal, not by a counter. |
|
|
23
|
+
| `premise-invalidated` | The plan premise moved: the situational-awareness fingerprint the run engaged under (`origin/main` plus every open PR head) was re-observed and differs. | Not `exhausted` — the budget is untouched. Not `stagnated` — progress may have been fine. Not `blocked` — nothing is missing; what the run knew went stale. |
|
|
24
|
+
|
|
25
|
+
**Why `premise-invalidated` is not `blocked` with a reason field.** Added
|
|
26
|
+
2026-09-03 (AI council, unanimous) when the continuation ladder needed a word for
|
|
27
|
+
a run whose base ref and open-PR set had moved under it. Reusing `blocked` keeps
|
|
28
|
+
the arity at six and loses two things that are not decorative: the ability to
|
|
29
|
+
COUNT premise invalidation separately from a missing precondition, and
|
|
30
|
+
type-enforced exhaustive handling in the consumers that switch on the state. The
|
|
31
|
+
remedies differ as sharply as `exhausted` and `stagnated` do — `blocked` says
|
|
32
|
+
obtain the missing thing, `premise-invalidated` says re-probe and re-plan against
|
|
33
|
+
the world as it now is.
|
|
34
|
+
|
|
35
|
+
A consumer that cannot represent it reports `blocked`, which is the DECLARED
|
|
36
|
+
downgrade in `_lib/outcome_vocabularies.ts` and not an ad-hoc substitution. The
|
|
37
|
+
value domain is versioned (`RUN_TERMINAL_VOCABULARY_VERSION`, v2), and a reader
|
|
38
|
+
meeting an unrecognised persisted value reports it as not-recorded rather than
|
|
39
|
+
crashing or guessing.
|
|
23
40
|
|
|
24
41
|
**Why `exhausted` and `stagnated` are separate**, when both stop a loop: they
|
|
25
42
|
have different remedies and different next actions. `exhausted` says *the work
|
|
@@ -40,8 +57,8 @@ budget-exhausted stop indistinguishable from a completed one in a dashboard.
|
|
|
40
57
|
| `[~]` | deferred | `blocked` or `approval-required`, undistinguished |
|
|
41
58
|
| `[-]` | cancelled | *(a decision, not a run outcome)* |
|
|
42
59
|
|
|
43
|
-
**
|
|
44
|
-
|
|
60
|
+
**Four states have no glyph, and they are exactly the ones a validation budget,
|
|
61
|
+
a hard-blocker class and a drift detector produce:**
|
|
45
62
|
|
|
46
63
|
1. **`exhausted`** — the run stopped at its cap with the step still open. The
|
|
47
64
|
glyph stays `[ ]`, which is identical to *never attempted*. A reader cannot
|
|
@@ -51,6 +68,8 @@ and a hard-blocker class produce:**
|
|
|
51
68
|
3. **`approval-required`** — collapses into `[~]` alongside `blocked`, so a
|
|
52
69
|
roadmap cannot show that the work is FINISHED and waiting on a human, versus
|
|
53
70
|
not startable at all.
|
|
71
|
+
4. **`premise-invalidated`** — same `[ ]` again, and the reader cannot see that
|
|
72
|
+
the step was not attempted because the plan it belonged to went stale.
|
|
54
73
|
|
|
55
74
|
**This is recorded rather than fixed.** Adding glyphs would change a format that
|
|
56
75
|
`update_roadmap_progress`, `check_roadmap_trackable`, `lint_empty_roadmaps` and
|
|
@@ -75,11 +94,13 @@ applies unchanged.
|
|
|
75
94
|
|
|
76
95
|
## Reporting contract
|
|
77
96
|
|
|
78
|
-
A closing report names the state **by one of the
|
|
97
|
+
A closing report names the state **by one of the seven words above**, and where the
|
|
79
98
|
state is not `success` it names what would change it:
|
|
80
99
|
|
|
81
100
|
- `exhausted` — the budget that ran out, its value, and what was achieved inside it.
|
|
82
101
|
- `stagnated` — the repeating failure signature, and the attempts it survived.
|
|
102
|
+
- `premise-invalidated` — which fingerprint was claimed, which was observed, and
|
|
103
|
+
that the next act is a re-probe rather than another iteration.
|
|
83
104
|
- `blocked` — the missing thing, by name.
|
|
84
105
|
- `approval-required` — the exact action awaiting approval.
|
|
85
106
|
- `clean-no-op` — what was scanned, so "nothing to do" is distinguishable from
|
|
@@ -22,6 +22,9 @@ collision_ok:
|
|
|
22
22
|
"implement": "fresh implementation is where over-commenting lands"
|
|
23
23
|
# obligation: line 5
|
|
24
24
|
obligation_frequency: "per-edit"
|
|
25
|
+
enforced_by:
|
|
26
|
+
- "validator:src/scripts/lint_code_comments.ts"
|
|
27
|
+
- "hook:comment-discipline"
|
|
25
28
|
---
|
|
26
29
|
|
|
27
30
|
# Code Comment Discipline
|
|
@@ -37,6 +40,9 @@ NO DOCBLOCK THAT ONLY MIRRORS THE NATIVE SIGNATURE.
|
|
|
37
40
|
DOCBLOCKS EARN THEIR PLACE ONLY WITH MACHINE-RELEVANT PRECISION
|
|
38
41
|
(GENERICS, ARRAY SHAPES, NON-TRIVIAL UNIONS) OR GENUINE WHY-CONTEXT.
|
|
39
42
|
WHEN IN DOUBT: NO COMMENT. SHORTER IS BETTER. NONE IS OFTEN BEST.
|
|
43
|
+
A COMMENT NAMING WHERE THE CODE CAME FROM — A ROADMAP PATH, A PHASE AND STEP,
|
|
44
|
+
A PROTOTYPE — IS EVIDENCE IN THE WRONG PLACE. PROVENANCE IS NOT A WHY.
|
|
45
|
+
A ROADMAP CANNOT LICENSE A COMMENT THIS RULE FORBIDS.
|
|
40
46
|
```
|
|
41
47
|
|
|
42
48
|
## What a comment is FOR
|
|
@@ -31,8 +31,8 @@ and dropped **402 entries** from the model-visible skills list
|
|
|
31
31
|
projection moved that to 330 and stripped the descriptions anyway.
|
|
32
32
|
|
|
33
33
|
402 is the **host's own count against the host's own denominator**, which is not
|
|
34
|
-
this package's:
|
|
35
|
-
magnitude, never a subtraction anyone can perform. What it establishes is that
|
|
34
|
+
this package's: that install projected 297 skills <!-- count: dated -->, so the figure
|
|
35
|
+
is a magnitude, never a subtraction anyone can perform. What it establishes is that
|
|
36
36
|
most of the catalogue is not model-visible — which is all this rule needs.
|
|
37
37
|
|
|
38
38
|
So "I do not see a skill for this" is evidence about the **delivery**, never
|
|
@@ -57,8 +57,8 @@ A skill claiming `rich` MUST satisfy ALL three:
|
|
|
57
57
|
carry a `load_context` pointer instead.
|
|
58
58
|
|
|
59
59
|
3. **≤ 15 % of the suite's skills may claim `rich`** — the CI linter counts
|
|
60
|
-
rich-tagged skills and hard-fails if the ratio is exceeded. With
|
|
61
|
-
|
|
60
|
+
rich-tagged skills and hard-fails if the ratio is exceeded. With 299 skills
|
|
61
|
+
the cap is ~44. Do not claim `rich` for convenience; claim it only for
|
|
62
62
|
irreducible-complexity skills.
|
|
63
63
|
|
|
64
64
|
## Telegraph-speak amendment
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
* `urp.collect()` completion criterion, and exit code 0. snake_case kept.
|
|
13
13
|
*
|
|
14
14
|
* A roadmap that has reached `count_open == 0` and `count_deferred == 0` is
|
|
15
|
-
* **complete**. This sweep moves it to `agents/roadmaps/archive/`, rewrites
|
|
16
|
-
* inbound references (`agents/roadmaps/<x>.md` → `agents/roadmaps/archive/<x>.md`)
|
|
15
|
+
* **complete**. This sweep moves it to `agents/roadmaps/archive/`, rewrites code-comment-allow provenance-comment -- the sweep's own operand
|
|
16
|
+
* inbound references (`agents/roadmaps/<x>.md` → `agents/roadmaps/archive/<x>.md`) code-comment-allow provenance-comment -- the rewrite this sweep performs
|
|
17
17
|
* across tracked files so links never break, and regenerates the dashboard.
|
|
18
18
|
*
|
|
19
19
|
* It replaces the old **merge-gate** (keep one item open + a manual post-merge
|
|
@@ -136,7 +136,7 @@ function _branch_touched_paths(root: string, base: string): Set<string> | null {
|
|
|
136
136
|
*
|
|
137
137
|
* Two classes, and the second is why this predicate exists at all:
|
|
138
138
|
*
|
|
139
|
-
* - `agents/evidence/**` — a committed record of what was true, or of what a
|
|
139
|
+
* - `agents/evidence/**` — a committed record of what was true, or of what a code-comment-allow provenance-comment -- the predicate's operand set
|
|
140
140
|
* reviewer was shown, at one point in time. Retroactively editing it makes it
|
|
141
141
|
* claim something that did not happen, which is the failure
|
|
142
142
|
* `evaluator-independence` exists to prevent ("the prompt ships with the
|
|
@@ -146,7 +146,7 @@ function _branch_touched_paths(root: string, base: string): Set<string> | null {
|
|
|
146
146
|
* AND makes `git apply` target a path the diff never created.
|
|
147
147
|
*
|
|
148
148
|
* Measured 2026-08-11: archiving one roadmap rewrote four frozen artifacts
|
|
149
|
-
* under `agents/evidence/reviews/`, including the `diff.patch` headers of a
|
|
149
|
+
* under `agents/evidence/reviews/`, including the `diff.patch` headers of a code-comment-allow provenance-comment -- names what the predicate protects
|
|
150
150
|
* recorded review input. Nothing caught it — the sweep reported it as
|
|
151
151
|
* "4 ref(s) migrated", which reads like success.
|
|
152
152
|
*/
|
|
@@ -346,7 +346,7 @@ function _escapeRe(v: string): string {
|
|
|
346
346
|
* bare `- <name>` item inside the `relates:` block, which is a declaration the
|
|
347
347
|
* author made deliberately. A mention anywhere else in the file does not count.
|
|
348
348
|
*/
|
|
349
|
-
function _relatesTo(text: string, slug: string): boolean {
|
|
349
|
+
export function _relatesTo(text: string, slug: string): boolean {
|
|
350
350
|
const lines = text.split('\n');
|
|
351
351
|
const start = lines.findIndex((l) => /^relates:/.test(l));
|
|
352
352
|
if (start === -1) return false;
|
|
@@ -654,7 +654,7 @@ function _regen_dashboard(root: string, dry_run: boolean): void {
|
|
|
654
654
|
/**
|
|
655
655
|
* Rebuild the archive index after a move, when the builder is present.
|
|
656
656
|
*
|
|
657
|
-
* The sweep is the one operation that changes what `agents/roadmaps/archive/`
|
|
657
|
+
* The sweep is the one operation that changes what `agents/roadmaps/archive/` code-comment-allow provenance-comment -- the directory this function writes
|
|
658
658
|
* contains, so it is the one operation that can leave the generated index
|
|
659
659
|
* stale — and the drift gate on that index runs in the same PR the sweep runs
|
|
660
660
|
* in. Regenerating here keeps the two from disagreeing by construction rather
|
|
@@ -226,9 +226,18 @@ export function guardedBaselineProblems(items: readonly GuardedBaselineItem[]):
|
|
|
226
226
|
*
|
|
227
227
|
* HONEST SCOPE. Only a PATH trigger is machine-checkable: a token containing `/`
|
|
228
228
|
* is resolved against the repo root and stale iff it exists. A bare symbol name
|
|
229
|
-
* cannot be decided from a path, so
|
|
230
|
-
* silently reading as "not stale" — an
|
|
231
|
-
* the failure this whole state exists
|
|
229
|
+
* cannot be decided from a path, so a record carrying ONLY symbol tokens is
|
|
230
|
+
* returned as `unverifiable` rather than silently reading as "not stale" — an
|
|
231
|
+
* unchecked trigger that looks checked is the failure this whole state exists
|
|
232
|
+
* to avoid.
|
|
233
|
+
*
|
|
234
|
+
* A record whose trigger carries AT LEAST ONE path token is decided by that
|
|
235
|
+
* path and is NOT reported as unverifiable, even when a companion symbol token
|
|
236
|
+
* sits beside it. Reporting it would be the mirror of the failure above: a
|
|
237
|
+
* checked trigger that looks unchecked. Measured 2026-09-01 — three of the four
|
|
238
|
+
* lines the dashboard printed as "not machine-checkable" carried a path token
|
|
239
|
+
* and were therefore already decidable, which is noise that trains a reader to
|
|
240
|
+
* skip the section where the one genuinely undecidable trigger also lives.
|
|
232
241
|
*/
|
|
233
242
|
export function guardedBaselineStaleness(
|
|
234
243
|
items: readonly GuardedBaselineItem[],
|
|
@@ -241,16 +250,22 @@ export function guardedBaselineStaleness(
|
|
|
241
250
|
if (trigger === '') {
|
|
242
251
|
continue;
|
|
243
252
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
253
|
+
const tokens = trigger.split(/[\s,]+/).filter((t) => t !== '');
|
|
254
|
+
const paths = tokens.filter((t) => t.includes('/'));
|
|
255
|
+
for (const token of paths) {
|
|
256
|
+
if (fs.existsSync(path.join(repo_root, token))) {
|
|
248
257
|
stale.push(
|
|
249
258
|
`line ${it.line} (${it.label}): recheck_when \`${token}\` now exists — ` +
|
|
250
259
|
're-verify against the real mechanism',
|
|
251
260
|
);
|
|
252
261
|
}
|
|
253
262
|
}
|
|
263
|
+
if (paths.length > 0) {
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
for (const token of tokens) {
|
|
267
|
+
unverifiable.push(`line ${it.line} (${it.label}): recheck_when \`${token}\``);
|
|
268
|
+
}
|
|
254
269
|
}
|
|
255
270
|
return { stale, unverifiable };
|
|
256
271
|
}
|
|
@@ -66,6 +66,48 @@ export interface StubRecord {
|
|
|
66
66
|
owner_decisions: number;
|
|
67
67
|
overdue: boolean;
|
|
68
68
|
days_overdue: number | null;
|
|
69
|
+
/** Frontmatter `design_validated:` — a citation, or null when absent. */
|
|
70
|
+
design_validated: string | null;
|
|
71
|
+
/** Frontmatter `capability_gap:` — `none` means the stub declares no gap. */
|
|
72
|
+
capability_gap: string | null;
|
|
73
|
+
/** Frontmatter `blocker_class:` — `estate` | `budget` | `product`. */
|
|
74
|
+
blocker_class: string | null;
|
|
75
|
+
/** Frontmatter `blocker_opened:` — ISO date the hold began, or null. */
|
|
76
|
+
blocker_opened: string | null;
|
|
77
|
+
/** Releases published since `blocker_opened`, or null when it is absent. */
|
|
78
|
+
releases_since_blocked: number | null;
|
|
79
|
+
/** All three membership conditions hold. See {@link BLOCKED_QUICKWIN_FIELDS}. */
|
|
80
|
+
blocked_quickwin: boolean;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The three frontmatter keys that decide fourth-bucket membership.
|
|
85
|
+
*
|
|
86
|
+
* Read as SCALARS, never as substrings of the body. A substring match over
|
|
87
|
+
* prose is what put a validated fix and a waiting preference into one bucket
|
|
88
|
+
* in the first place; doing it with three substrings instead of one would be
|
|
89
|
+
* worse, not better. A stub missing any of the three is not a member and falls
|
|
90
|
+
* through to the OWNER bucket exactly as before — absence is never a member.
|
|
91
|
+
*/
|
|
92
|
+
export const BLOCKED_QUICKWIN_FIELDS = ['design_validated', 'capability_gap', 'blocker_class'] as const;
|
|
93
|
+
|
|
94
|
+
/** Blocker classes that make a stub a BLOCKED QUICK-WIN rather than a product wait. */
|
|
95
|
+
export const QUICKWIN_BLOCKER_CLASSES = ['estate', 'budget'] as const;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* All three conditions, read from frontmatter only.
|
|
99
|
+
*
|
|
100
|
+
* A recorded design validation, an explicit `capability_gap: none`, and an open
|
|
101
|
+
* blocker that is an estate or budget decision rather than a product one.
|
|
102
|
+
*/
|
|
103
|
+
export function is_blocked_quickwin(
|
|
104
|
+
design_validated: string | null,
|
|
105
|
+
capability_gap: string | null,
|
|
106
|
+
blocker_class: string | null,
|
|
107
|
+
): boolean {
|
|
108
|
+
if (design_validated === null || design_validated === '') return false;
|
|
109
|
+
if (capability_gap !== 'none') return false;
|
|
110
|
+
return (QUICKWIN_BLOCKER_CLASSES as readonly string[]).includes(blocker_class ?? '');
|
|
69
111
|
}
|
|
70
112
|
|
|
71
113
|
function _isDir(p: string): boolean {
|
|
@@ -188,7 +230,48 @@ function _parseIso(s: string): Date | null {
|
|
|
188
230
|
|
|
189
231
|
/** Read every stub and classify it. `today` is injectable so tests are stable. */
|
|
190
232
|
export function scan(root: string, today: string): StubRecord[] {
|
|
191
|
-
return scan_dir(path.join(root, STUB_DIR), today);
|
|
233
|
+
return scan_dir(path.join(root, STUB_DIR), today, release_dates(root));
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Changelog files that carry released sections, newest era first.
|
|
238
|
+
*
|
|
239
|
+
* BOTH are read, and that is the whole point of the pair. The era split moved
|
|
240
|
+
* older releases into the archive, so a count taken over the current file alone
|
|
241
|
+
* silently reports zero for anything before the split — measured: 14.11.0 lives
|
|
242
|
+
* only in the archive, and grepping `CHANGELOG.md` for it returns nothing.
|
|
243
|
+
*/
|
|
244
|
+
export const CHANGELOG_ERAS = ['CHANGELOG.md', 'docs/archive/CHANGELOG-pre-14.12.0.md'] as const;
|
|
245
|
+
|
|
246
|
+
/** `## [x.y.z](…) (YYYY-MM-DD)` — a released section heading with its date. */
|
|
247
|
+
const RELEASE_HEADING = /^## \[\d+\.\d+\.\d+\][^\n]*\((\d{4}-\d{2}-\d{2})\)\s*$/gm;
|
|
248
|
+
|
|
249
|
+
/** Every released section's date across both eras, unsorted. */
|
|
250
|
+
export function release_dates(root: string): string[] {
|
|
251
|
+
const out: string[] = [];
|
|
252
|
+
for (const rel of CHANGELOG_ERAS) {
|
|
253
|
+
let text: string;
|
|
254
|
+
try {
|
|
255
|
+
text = fs.readFileSync(path.join(root, rel), 'utf8');
|
|
256
|
+
} catch {
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
for (const m of text.matchAll(RELEASE_HEADING)) {
|
|
260
|
+
if (m[1] !== undefined) out.push(m[1]);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return out;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Releases published strictly AFTER the day the hold began.
|
|
268
|
+
*
|
|
269
|
+
* Strictly after, because a release cut on the same day cannot have carried a
|
|
270
|
+
* fix the hold was blocking — counting it would inflate the figure the
|
|
271
|
+
* council's falsifier is read against.
|
|
272
|
+
*/
|
|
273
|
+
export function releases_since(opened: string, dates: readonly string[]): number {
|
|
274
|
+
return dates.filter((d) => d > opened).length;
|
|
192
275
|
}
|
|
193
276
|
|
|
194
277
|
/**
|
|
@@ -198,7 +281,11 @@ export function scan(root: string, today: string): StubRecord[] {
|
|
|
198
281
|
* immediately re-join — a fragile round trip past exactly the directory both
|
|
199
282
|
* callers care about.
|
|
200
283
|
*/
|
|
201
|
-
export function scan_dir(
|
|
284
|
+
export function scan_dir(
|
|
285
|
+
base: string,
|
|
286
|
+
today: string,
|
|
287
|
+
releaseDates: readonly string[] = [],
|
|
288
|
+
): StubRecord[] {
|
|
202
289
|
if (!_isDir(base)) return [];
|
|
203
290
|
const now = _parseIso(today);
|
|
204
291
|
const out: StubRecord[] = [];
|
|
@@ -222,6 +309,10 @@ export function scan_dir(base: string, today: string): StubRecord[] {
|
|
|
222
309
|
const review_by = _scalar(front, 'review_by');
|
|
223
310
|
const reviewed_at = _scalar(front, 'reviewed_at');
|
|
224
311
|
const probeVal = _scalar(front, 'probe');
|
|
312
|
+
const design_validated = _scalar(front, 'design_validated');
|
|
313
|
+
const capability_gap = _scalar(front, 'capability_gap');
|
|
314
|
+
const blocker_class = _scalar(front, 'blocker_class');
|
|
315
|
+
const blocker_opened = _scalar(front, 'blocker_opened');
|
|
225
316
|
const shape: StubShape = text.includes(TRANSFER_MARKER) ? 'transfer' : 'orgmode';
|
|
226
317
|
let overdue = false;
|
|
227
318
|
let days_overdue: number | null = null;
|
|
@@ -242,6 +333,13 @@ export function scan_dir(base: string, today: string): StubRecord[] {
|
|
|
242
333
|
owner_decisions: count_owner_decisions(text),
|
|
243
334
|
overdue,
|
|
244
335
|
days_overdue,
|
|
336
|
+
design_validated,
|
|
337
|
+
capability_gap,
|
|
338
|
+
blocker_class,
|
|
339
|
+
blocker_opened,
|
|
340
|
+
releases_since_blocked:
|
|
341
|
+
blocker_opened === null ? null : releases_since(blocker_opened, releaseDates),
|
|
342
|
+
blocked_quickwin: is_blocked_quickwin(design_validated, capability_gap, blocker_class),
|
|
245
343
|
});
|
|
246
344
|
}
|
|
247
345
|
return out;
|
|
@@ -251,6 +349,8 @@ export interface Counts {
|
|
|
251
349
|
overdue: number;
|
|
252
350
|
owner_decisions: number;
|
|
253
351
|
missing_review_by: number;
|
|
352
|
+
/** ADDITIVE 2026-09-01. Existing keys are unchanged in name, type and meaning. */
|
|
353
|
+
blocked_quickwin: number;
|
|
254
354
|
total: number;
|
|
255
355
|
}
|
|
256
356
|
|
|
@@ -259,6 +359,7 @@ export function counts(records: StubRecord[]): Counts {
|
|
|
259
359
|
overdue: records.filter((r) => r.overdue).length,
|
|
260
360
|
owner_decisions: records.reduce((s, r) => s + r.owner_decisions, 0),
|
|
261
361
|
missing_review_by: records.filter((r) => r.review_by === null).length,
|
|
362
|
+
blocked_quickwin: records.filter((r) => r.blocked_quickwin).length,
|
|
262
363
|
total: records.length,
|
|
263
364
|
};
|
|
264
365
|
}
|
|
@@ -328,6 +429,21 @@ export function main(argv: readonly string[] = process.argv.slice(2)): number {
|
|
|
328
429
|
}
|
|
329
430
|
}
|
|
330
431
|
|
|
432
|
+
const quickwins = records.filter((r) => r.blocked_quickwin);
|
|
433
|
+
if (quickwins.length > 0) {
|
|
434
|
+
process.stdout.write(
|
|
435
|
+
`\n 🔓 ${quickwins.length} BLOCKED QUICK-WIN(S) — design validated, no capability gap,\n` +
|
|
436
|
+
' held by an estate or budget decision rather than a product one:\n',
|
|
437
|
+
);
|
|
438
|
+
for (const r of quickwins) {
|
|
439
|
+
const since =
|
|
440
|
+
r.releases_since_blocked === null
|
|
441
|
+
? 'no `blocker_opened:`'
|
|
442
|
+
: `${String(r.releases_since_blocked)} release(s) since ${r.blocker_opened}`;
|
|
443
|
+
process.stdout.write(` ${r.file} (${r.blocker_class}, ${since})\n`);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
331
447
|
const noProbe = records.filter((r) => !r.probe_none && !r.has_probe_heading);
|
|
332
448
|
if (noProbe.length > 0) {
|
|
333
449
|
process.stdout.write(
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* `archive_completed_roadmaps` can consume it the same way Python does. snake_case
|
|
13
13
|
* is kept on the public surface.
|
|
14
14
|
*
|
|
15
|
-
* Scans every roadmap under `agents/roadmaps/` (excluding `archive/`, `skipped/`,
|
|
15
|
+
* Scans every roadmap under `agents/roadmaps/` (excluding `archive/`, `skipped/`, code-comment-allow provenance-comment -- the path is this script's operand, not where the code came from
|
|
16
16
|
* `template.md`, `README.md`, `open-questions*.md`), counts checkbox states per
|
|
17
17
|
* phase, and writes a dashboard at `agents/roadmaps-progress.md` (outside the
|
|
18
18
|
* `roadmaps/` folder to keep it clean) with:
|
|
@@ -96,7 +96,7 @@ const EXCLUDE_DIRS: ReadonlySet<string> = new Set(['archive', 'skipped', 'stubs'
|
|
|
96
96
|
|
|
97
97
|
// FRONTMATTER_RE = re.compile(r"\A---\n(.*?)\n---\s*\n", re.DOTALL)
|
|
98
98
|
const FRONTMATTER_RE = /^---\n([\s\S]*?)\n---[ \t\n\r\f\v]*\n/;
|
|
99
|
-
const
|
|
99
|
+
const UNSCHEDULED_VALUES: ReadonlySet<string> = new Set(['draft', 'carrier']);
|
|
100
100
|
|
|
101
101
|
const MERGE_GATED_RE = /merge-gated/i;
|
|
102
102
|
// PR_NUM_RE = re.compile(r"pr\s*[=#:]?\s*#?\s*(\d+)", re.IGNORECASE)
|
|
@@ -289,8 +289,8 @@ function _stripQuotes(s: string): string {
|
|
|
289
289
|
return out;
|
|
290
290
|
}
|
|
291
291
|
|
|
292
|
-
function
|
|
293
|
-
return
|
|
292
|
+
function is_unscheduled(fm: Record<string, string>): boolean {
|
|
293
|
+
return UNSCHEDULED_VALUES.has((fm['status'] ?? '').toLowerCase());
|
|
294
294
|
}
|
|
295
295
|
|
|
296
296
|
/**
|
|
@@ -752,7 +752,7 @@ function collect(roadmap_root: string): RoadmapStats[] {
|
|
|
752
752
|
continue;
|
|
753
753
|
}
|
|
754
754
|
const text = fs.readFileSync(p, { encoding: 'utf-8' });
|
|
755
|
-
if (
|
|
755
|
+
if (is_unscheduled(parse_frontmatter(text))) {
|
|
756
756
|
continue;
|
|
757
757
|
}
|
|
758
758
|
const stats = parse_roadmap(p, roadmap_root);
|
|
@@ -820,7 +820,7 @@ function collect_parked(roadmap_root: string): ParkedRoadmap[] {
|
|
|
820
820
|
} catch {
|
|
821
821
|
continue;
|
|
822
822
|
}
|
|
823
|
-
if (
|
|
823
|
+
if (is_unscheduled(parse_frontmatter(text))) {
|
|
824
824
|
continue;
|
|
825
825
|
}
|
|
826
826
|
const cell = resume_cell(text);
|
|
@@ -1305,7 +1305,7 @@ function _parseArgs(argv: readonly string[]): Args {
|
|
|
1305
1305
|
}
|
|
1306
1306
|
|
|
1307
1307
|
/**
|
|
1308
|
-
* When the default cwd carries no `agents/roadmaps/`, fall back to the git
|
|
1308
|
+
* When the default cwd carries no `agents/roadmaps/`, fall back to the git code-comment-allow provenance-comment -- the path is this script's operand, not where the code came from
|
|
1309
1309
|
* toplevel — hook and IDE invocations often run from a subdirectory, and a
|
|
1310
1310
|
* cwd-only resolution silently skipped the dashboard there. An explicit
|
|
1311
1311
|
* `--repo-root` always wins; a cwd that has the directory is used as-is
|
|
@@ -1479,7 +1479,7 @@ export {
|
|
|
1479
1479
|
TITLE_RE,
|
|
1480
1480
|
FRONTMATTER_RE,
|
|
1481
1481
|
parse_frontmatter,
|
|
1482
|
-
|
|
1482
|
+
is_unscheduled,
|
|
1483
1483
|
is_roadmap_candidate,
|
|
1484
1484
|
count_checkboxes,
|
|
1485
1485
|
parse_blockers,
|
|
@@ -39,7 +39,7 @@ schema_version: 1
|
|
|
39
39
|
# CI guard: a release bump of `package.json` must update this value
|
|
40
40
|
# in lockstep — see scripts/check_template_pin_drift.ts (road-to-
|
|
41
41
|
# portable-runtime-and-update-check P3.3).
|
|
42
|
-
agent_config_version: "14.
|
|
42
|
+
agent_config_version: "14.14.1"
|
|
43
43
|
|
|
44
44
|
# --- Project identity ---
|
|
45
45
|
project:
|
package/dist/cli/registry.js
CHANGED
|
@@ -77,6 +77,7 @@ export const REGISTRY = [
|
|
|
77
77
|
{ name: 'hooks:install', disposition: 'delegate', synopsis: 'Install the combined pre-commit hook.' },
|
|
78
78
|
{ name: 'hooks:status', disposition: 'delegate', synopsis: 'Print the runtime hook matrix (per-platform install + bindings).' },
|
|
79
79
|
{ name: 'hooks:doctor', disposition: 'delegate', synopsis: 'Diagnose hook health: concerns, posture, missing trampolines (read-only).' },
|
|
80
|
+
{ name: 'hooks:effect', disposition: 'delegate', synopsis: 'Does a bound concern actually FIRE here? Sandboxed synthetic dispatch per concern; five per-concern states, four host verdicts (read-only).' },
|
|
80
81
|
{ name: 'routing:doctor', disposition: 'delegate', synopsis: 'Live routing diagnosis: per-gate ACTIVE/INACTIVE with reason, chain, freshness, orchestration state (read-only).' },
|
|
81
82
|
{ name: 'workspace:doctor', disposition: 'delegate', synopsis: 'Workspace identity with provenance (repo root, main worktree, current worktree, branch, PR base) + session claim and worktree pressure (read-only).' },
|
|
82
83
|
{ name: 'route:explain', disposition: 'delegate', synopsis: 'Deterministic rule-routing trace for one prompt: matched triggers, tier, disposition, budget, rejected candidates (trigger-match level only).' },
|