@event4u/agent-config 14.20.0 → 14.22.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/.augment-plugin/marketplace.json +3 -3
- package/.augment-plugin/plugin.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +441 -0
- package/CREDITS.md +37 -0
- package/README.md +1 -1
- package/agents/templates/.ai-council.yml.example +10 -0
- package/dist/agent-src/commands/analyze/inbox.md +381 -6
- package/dist/agent-src/commands/analyze/{reference-repo.md → repo.md} +251 -34
- package/dist/agent-src/commands/analyze/roadmap-repos.md +129 -0
- package/dist/agent-src/commands/analyze.md +12 -7
- package/dist/agent-src/commands/feature/plan.md +55 -16
- package/dist/agent-src/commands/feature/refactor.md +8 -3
- package/dist/agent-src/commands/humanize.md +48 -4
- package/dist/agent-src/contexts/communication/rules-auto/token-efficiency-mechanics.md +69 -0
- package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +5 -5
- package/dist/agent-src/contexts/execution/contract-decision-sheet.md +22 -1
- package/dist/agent-src/contexts/execution/host-capability-manifest.md +4 -1
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +35 -2
- package/dist/agent-src/contexts/execution/terminal-states.md +4 -1
- package/dist/agent-src/rules/evaluator-independence.md +4 -4
- package/dist/agent-src/rules/external-code-graph-interop.md +19 -14
- package/dist/agent-src/rules/external-reference-deep-dive.md +2 -2
- package/dist/agent-src/rules/token-efficiency.md +26 -3
- package/dist/agent-src/scripts/_cli_entry.ts +51 -0
- package/dist/agent-src/scripts/archive_completed_roadmaps.ts +14 -23
- package/dist/agent-src/scripts/roadmap_gates.ts +4 -20
- package/dist/agent-src/scripts/roadmap_set_step.ts +5 -2
- package/dist/agent-src/scripts/stubs_due.ts +4 -18
- package/dist/agent-src/scripts/update_roadmap_progress.ts +7 -24
- package/dist/agent-src/skills/code-intelligence/SKILL.md +25 -0
- package/dist/agent-src/skills/competitive-positioning/SKILL.md +2 -2
- package/dist/agent-src/skills/humanizer/references/anti-aiisms.md +27 -11
- package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +1 -1
- package/dist/agent-src/skills/markitdown/SKILL.md +1 -1
- package/dist/agent-src/skills/repomix-packer/SKILL.md +1 -1
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +17 -0
- package/dist/agent-src/skills/technical-specification/SKILL.md +4 -0
- package/dist/agent-src/templates/agent-settings.md +0 -1
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/features.md +5 -0
- package/dist/agent-src/templates/roadmaps.md +15 -0
- package/dist/cli-delegate/_cli_entry.js +8 -0
- package/dist/cli-delegate/_lib/pin_resolver.js +170 -0
- package/dist/cli-delegate/archival_sweep.js +8 -0
- package/dist/cli-delegate/archive_completed_roadmaps.js +494 -0
- package/dist/cli-delegate/check_update_banner.js +923 -0
- package/dist/cli-delegate/{chunk-KPBWFOPZ.js → chunk-2FTMENJL.js} +194 -37
- package/dist/cli-delegate/{chunk-PI2GU3DP.js → chunk-2TAQ2K4V.js} +4 -1
- package/dist/cli-delegate/chunk-5EK5WQN6.js +28 -0
- package/dist/cli-delegate/chunk-7XGD2OEU.js +96 -0
- package/dist/cli-delegate/{chunk-C6IPZRDA.js → chunk-BIXEDQPZ.js} +7 -1
- package/dist/cli-delegate/chunk-DBH4AHDK.js +155 -0
- package/dist/cli-delegate/{chunk-NUMQHLS5.js → chunk-J3RFNKG4.js} +21 -2
- package/dist/cli-delegate/chunk-JO25G5H7.js +20 -0
- package/dist/cli-delegate/chunk-LTYVIY76.js +7961 -0
- package/dist/cli-delegate/chunk-QAJS5YWY.js +999 -0
- package/dist/cli-delegate/{chunk-ZVBOHSVF.js → chunk-TGIPZBCW.js} +1 -1
- package/dist/cli-delegate/{chunk-YS6XTLEK.js → chunk-TYSY33MD.js} +56 -6
- package/dist/cli-delegate/chunk-ULET4BYS.js +328 -0
- package/dist/cli-delegate/chunk-YDQSHTD7.js +187 -0
- package/dist/cli-delegate/chunk-YWVXLR44.js +48 -0
- package/dist/cli-delegate/chunk-ZZQBUHFK.js +273 -0
- package/dist/cli-delegate/cmd_conformance.js +19 -5
- package/dist/cli-delegate/cmd_doctor.js +3 -3
- package/dist/cli-delegate/cmd_export.js +2 -2
- package/dist/cli-delegate/cmd_handoff.js +27 -3
- package/dist/cli-delegate/cmd_prune.js +2 -2
- package/dist/cli-delegate/cmd_session_recycle.js +2 -2
- package/dist/cli-delegate/cmd_settings_migrate.js +2 -2
- package/dist/cli-delegate/cmd_sync.js +2 -2
- package/dist/cli-delegate/cmd_uninstall.js +2 -2
- package/dist/cli-delegate/cmd_validate.js +2 -2
- package/dist/cli-delegate/dashboard_mode.js +16 -0
- package/dist/cli-delegate/gate_budget.js +24 -0
- package/dist/cli-delegate/gate_execute.js +25 -0
- package/dist/cli-delegate/guarded_baseline.js +18 -0
- package/dist/cli-delegate/resume_probe.js +22 -0
- package/dist/cli-delegate/roadmap_gates.js +638 -0
- package/dist/cli-delegate/roadmap_set_step.js +173 -0
- package/dist/cli-delegate/stub_queue.js +47 -0
- package/dist/cli-delegate/stubs_due.js +38 -0
- package/dist/cli-delegate/ticket_bundles.js +59 -0
- package/dist/cli-delegate/update_roadmap_progress.js +60 -0
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +60 -29
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +3 -3
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +7 -6
- package/dist/discovery/trust-report.md +2 -2
- package/dist/discovery/workspaces.json +6 -5
- package/dist/hooks/dispatch.js +1522 -1008
- package/dist/install/claudeLayerCarriage.js +237 -0
- package/dist/install/claudePathsPlan.js +79 -0
- package/dist/install/hostLayerFingerprint.js +1 -1
- package/dist/install/install.mjs +10 -1
- package/dist/install/partitionEligibility.js +231 -117
- package/dist/install/ruleLayerPartition.js +10 -7
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +39376 -38902
- package/dist/scripts/ai_council/content_ceiling.js +63 -0
- package/dist/scripts/ai_council/seat_policy.js +106 -0
- package/dist/scripts/ai_council/transport_resolver.js +21 -0
- package/dist/scripts/condense.js +32 -25
- package/docs/MIGRATION.md +49 -1
- package/docs/contracts/settings-classes.md +1 -1
- package/docs/guidelines/agent-infra/minimal-safe-diff-mechanics.md +73 -0
- package/llms.txt +14 -14
- package/package.json +7 -4
- package/src/agent-src/scripts/_cli_entry.ts +51 -0
- package/src/agent-src/scripts/archive_completed_roadmaps.ts +14 -23
- package/src/agent-src/scripts/roadmap_gates.ts +4 -20
- package/src/agent-src/scripts/roadmap_set_step.ts +5 -2
- package/src/agent-src/scripts/stubs_due.ts +4 -18
- package/src/agent-src/scripts/update_roadmap_progress.ts +7 -24
- package/src/config/continuity-surface.json +509 -0
- package/src/config/estate-count-budget.json +1 -1
- package/src/config/gate-coverage.yml +29 -8
- package/src/config/gate-violation-baselines.json +8 -8
- package/src/config/gitignore-block.txt +19 -0
- package/src/config/host-injection-effect.json +73 -0
- package/src/config/packed-binary-manifest.json +33 -0
- package/src/config/preamble-payload-budget.json +112 -105
- package/src/config/publish-surface.json +4 -2
- package/src/config/release-gate-locality.yml +4 -3
- package/src/install/claudeLayerCarriage.ts +273 -0
- package/src/install/claudePathsPlan.ts +76 -0
- package/src/install/hostLayerFingerprint.ts +1 -1
- package/src/install/partitionEligibility.ts +261 -127
- package/src/install/ruleLayerPartition.ts +19 -23
- package/src/scripts/_dispatch.bash +77 -27
- package/src/scripts/_lib/auto_dispatch.ts +2 -2
- package/src/scripts/_lib/blocked_by_marker.ts +189 -0
- package/src/scripts/_lib/catalogue_layer_parity.ts +146 -0
- package/src/scripts/_lib/continuity_surface.ts +296 -0
- package/src/scripts/_lib/corpus_manifest.ts +26 -9
- package/src/scripts/_lib/hook_settings.ts +1 -1
- package/src/scripts/_lib/host_capability.ts +38 -1
- package/src/scripts/_lib/injection_effect.ts +151 -0
- package/src/scripts/_lib/layer_overlap_notice.ts +50 -6
- package/src/scripts/_lib/packed_binary_predicate.ts +225 -0
- package/src/scripts/_lib/pin_resolver.ts +13 -0
- package/src/scripts/_lib/regression_neighbourhood.ts +1 -1
- package/src/scripts/_lib/release_highlights.ts +19 -211
- package/src/scripts/_lib/release_material.ts +27 -77
- package/src/scripts/_lib/retired_status.ts +74 -0
- package/src/scripts/_lib/skill_catalogue.ts +156 -21
- package/src/scripts/_lib/structured_ask.ts +117 -0
- package/src/scripts/_lib/subagent_capsule.ts +117 -8
- package/src/scripts/_lib/tty_prompt.ts +76 -0
- package/src/scripts/ai_council/api_transport.ts +168 -0
- package/src/scripts/ai_council/clients.ts +58 -63
- package/src/scripts/ai_council/config.ts +13 -0
- package/src/scripts/ai_council/content_ceiling.ts +79 -0
- package/src/scripts/ai_council/deliberation_metrics.ts +353 -0
- package/src/scripts/ai_council/jury_aggregate.ts +178 -0
- package/src/scripts/ai_council/prompts.ts +9 -0
- package/src/scripts/ai_council/seat_policy.ts +137 -0
- package/src/scripts/ai_council/status_surface.ts +79 -0
- package/src/scripts/ai_council/synthesis_disagreement.ts +206 -0
- package/src/scripts/ai_council/transport_resolver.ts +59 -1
- package/src/scripts/ai_tells_rules.ts +446 -5
- package/src/scripts/ask_block_census.ts +499 -0
- package/src/scripts/bench_humanizer_eval.ts +264 -40
- package/src/scripts/bench_prose_tells_fp.ts +198 -0
- package/src/scripts/capture_skill_catalogue.ts +24 -11
- package/src/scripts/check_branch_work_committed.ts +234 -0
- package/src/scripts/check_candidate_lines.ts +499 -0
- package/src/scripts/check_continuity_surface.ts +475 -0
- package/src/scripts/check_dependency_floors.ts +13 -6
- package/src/scripts/check_estate_count.ts +40 -79
- package/src/scripts/check_pack_size.ts +263 -24
- package/src/scripts/check_release_highlights.ts +0 -157
- package/src/scripts/check_roadmap_trackable.ts +19 -16
- package/src/scripts/check_rule_layer_partition.ts +54 -34
- package/src/scripts/check_single_delivery.ts +97 -31
- package/src/scripts/check_update_banner.ts +13 -0
- package/src/scripts/code_graph/build.ts +138 -15
- package/src/scripts/code_graph/cli.ts +76 -8
- package/src/scripts/code_graph/extract.ts +11 -0
- package/src/scripts/code_graph/grammar_probe.ts +43 -0
- package/src/scripts/code_graph/loader.ts +88 -14
- package/src/scripts/code_graph/query.ts +121 -11
- package/src/scripts/code_graph/resolution_tiers.ts +238 -0
- package/src/scripts/code_graph/sqlite_store.ts +247 -6
- package/src/scripts/code_graph/types.ts +66 -1
- package/src/scripts/code_graph/validate.ts +23 -0
- package/src/scripts/condense.ts +34 -22
- package/src/scripts/corpus_manifest.ts +5 -5
- package/src/scripts/council_cli.ts +29 -39
- package/src/scripts/count_intent_disagreement.ts +426 -0
- package/src/scripts/detect_ai_tells.ts +142 -20
- package/src/scripts/handoff_context_hook.ts +79 -0
- package/src/scripts/harvest_reference_tokens.ts +698 -0
- package/src/scripts/hook_effect_doctor.ts +82 -1
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +94 -12
- package/src/scripts/hooks/chain_nudge_hook.ts +282 -0
- package/src/scripts/hooks/code_graph_context_hook.ts +212 -0
- package/src/scripts/hooks/concern_registry.ts +6 -2
- package/src/scripts/hooks/delegation_nudge_hook.ts +45 -1
- package/src/scripts/hooks/host_semantics.ts +1 -1
- package/src/scripts/hooks/one_question_per_ask_hook.ts +191 -0
- package/src/scripts/hooks/orchestration_record_hook.ts +2 -2
- package/src/scripts/hooks/rule_inject_hook.ts +1 -1
- package/src/scripts/hooks/skill_route_hook.ts +11 -4
- package/src/scripts/inbox_source_census.ts +322 -0
- package/src/scripts/install-hooks.sh +134 -0
- package/src/scripts/{lint_carrier_integrity.ts → lint_deferral_integrity.ts} +60 -214
- package/src/scripts/lint_plan_risk_register.ts +13 -17
- package/src/scripts/lint_roadmap_blockers.ts +51 -1
- package/src/scripts/mcp_server/suggest_skill.ts +72 -0
- package/src/scripts/mcp_server/tools.ts +2 -56
- package/src/scripts/media/lib/adapter-contract.md +7 -2
- package/src/scripts/media/lib/fixtures/flux/asset-0001.png +0 -0
- package/src/scripts/media/lib/fixtures/gemini-image/asset-0001.png +0 -0
- package/src/scripts/media/lib/fixtures/ideogram/asset-0001.png +0 -0
- package/src/scripts/migrate_command_suggestions.ts +1 -1
- package/src/scripts/probe_unblocked_ask.ts +135 -2
- package/src/scripts/rdp_candidates_delta.ts +276 -0
- package/src/scripts/rdp_quality_eval.ts +217 -19
- package/src/scripts/release.ts +26 -71
- package/src/scripts/release_drill.ts +6 -10
- package/src/scripts/release_env.ts +1 -0
- package/src/scripts/release_mix_taxonomy.json +3 -2
- package/src/scripts/release_publication.ts +34 -29
- package/src/scripts/report_host_injection_effect.ts +189 -0
- package/src/scripts/report_layer_overlap.ts +41 -0
- package/src/scripts/report_skill_menu_census.ts +341 -0
- package/src/scripts/self_review_gate.ts +409 -15
- package/src/scripts/skill_tools/score_skill_relevance.ts +70 -6
- package/src/scripts/skill_tools/suggest_skill_for_task.ts +37 -3
- package/src/vendor/grammars/README.md +61 -0
- package/src/vendor/grammars/tree-sitter-javascript.wasm +0 -0
- package/src/vendor/grammars/tree-sitter-php.wasm +0 -0
- package/src/vendor/grammars/tree-sitter-typescript.wasm +0 -0
- package/src/scripts/_lib/carrier_status.ts +0 -118
- package/src/scripts/_lib/estate_carrier_cases.ts +0 -72
- package/src/scripts/hooks/code_graph_nudge_hook.ts +0 -191
package/src/scripts/release.ts
CHANGED
|
@@ -126,6 +126,7 @@ import {
|
|
|
126
126
|
derive_category_hits,
|
|
127
127
|
render_derived_head_values,
|
|
128
128
|
} from './_lib/release_highlights.js';
|
|
129
|
+
import { canPrompt, promptLine } from './_lib/tty_prompt.js';
|
|
129
130
|
import { preflightPosition } from './_lib/release_position.js';
|
|
130
131
|
import {
|
|
131
132
|
RELEASE_HEAD_DEFAULT,
|
|
@@ -554,8 +555,8 @@ function render_changelog_entry(
|
|
|
554
555
|
|
|
555
556
|
// The curated head sits above the generated log, which stays unchanged.
|
|
556
557
|
const body_lines: string[] = [...render_release_head(opts.head ?? {})];
|
|
557
|
-
// Under the head
|
|
558
|
-
if (opts.mix
|
|
558
|
+
// Under the head, outside it; any resolved reading — see `MixObligation`.
|
|
559
|
+
if (opts.mix) {
|
|
559
560
|
body_lines.push('');
|
|
560
561
|
body_lines.push(...render_mix_response(opts.mix.level));
|
|
561
562
|
}
|
|
@@ -933,74 +934,12 @@ function print_preview(plan: Plan): void {
|
|
|
933
934
|
}
|
|
934
935
|
|
|
935
936
|
function confirm(prompt: string): boolean | null {
|
|
936
|
-
const ans =
|
|
937
|
+
const ans = promptLine(`${prompt} [y/N] `);
|
|
937
938
|
if (ans === null) return null; // no usable controlling terminal
|
|
938
939
|
const norm = ans.trim().toLowerCase();
|
|
939
940
|
return norm === 'y' || norm === 'yes';
|
|
940
941
|
}
|
|
941
942
|
|
|
942
|
-
/** Can we prompt at all — is fd 0 a TTY, or is a controlling terminal openable? */
|
|
943
|
-
function _canPrompt(): boolean {
|
|
944
|
-
if (process.env.CI) return false; // CI is non-interactive by contract → require --yes
|
|
945
|
-
if (process.stdin.isTTY) return true;
|
|
946
|
-
try {
|
|
947
|
-
const fd = fs.openSync('/dev/tty', 'r');
|
|
948
|
-
fs.closeSync(fd);
|
|
949
|
-
return true;
|
|
950
|
-
} catch {
|
|
951
|
-
return false;
|
|
952
|
-
}
|
|
953
|
-
}
|
|
954
|
-
|
|
955
|
-
/**
|
|
956
|
-
* Mirror of Python `input(prompt)` — write the prompt, read one line from the
|
|
957
|
-
* controlling terminal. Returns `null` when no terminal is reachable.
|
|
958
|
-
*
|
|
959
|
-
* The answer is ALWAYS read from a freshly opened, blocking `/dev/tty`
|
|
960
|
-
* (`openSync(..., 'rs')`), NEVER from fd 0. `task release` / `./scripts-run`
|
|
961
|
-
* run this under go-task's `interactive: true`, which leaves fd 0 a TTY
|
|
962
|
-
* (`process.stdin.isTTY === true`) but one Node treats as NON-blocking — so a
|
|
963
|
-
* bare `readSync(0)` throws `EAGAIN`, the catch swallows it, and the `[y/N]`
|
|
964
|
-
* prompt "auto-aborts" without ever waiting (the exact failure this fixes;
|
|
965
|
-
* reproduced with `isTTY === true`). A fresh blocking `/dev/tty` descriptor
|
|
966
|
-
* blocks for real input regardless of how the script was invoked — the proven
|
|
967
|
-
* synchronous-prompt pattern.
|
|
968
|
-
*/
|
|
969
|
-
function _input(prompt: string): string | null {
|
|
970
|
-
process.stdout.write(prompt);
|
|
971
|
-
let fd: number;
|
|
972
|
-
try {
|
|
973
|
-
// 'rs' → O_RDONLY | O_SYNC: a fresh, blocking descriptor on the
|
|
974
|
-
// controlling terminal, unaffected by Node's non-blocking fd 0.
|
|
975
|
-
fd = fs.openSync('/dev/tty', 'rs');
|
|
976
|
-
} catch {
|
|
977
|
-
return null; // no controlling terminal (true non-interactive)
|
|
978
|
-
}
|
|
979
|
-
try {
|
|
980
|
-
const buf = Buffer.alloc(1);
|
|
981
|
-
const chars: number[] = [];
|
|
982
|
-
for (;;) {
|
|
983
|
-
let bytesRead: number;
|
|
984
|
-
try {
|
|
985
|
-
bytesRead = fs.readSync(fd, buf, 0, 1, null);
|
|
986
|
-
} catch (e) {
|
|
987
|
-
const code = (e as NodeJS.ErrnoException).code;
|
|
988
|
-
// A blocking /dev/tty should not yield these, but inherited
|
|
989
|
-
// descriptor flags can — retry rather than abort the prompt.
|
|
990
|
-
if (code === 'EAGAIN' || code === 'EINTR') continue;
|
|
991
|
-
break; // EOF / EIO → EOFError analogue; return what we have.
|
|
992
|
-
}
|
|
993
|
-
if (bytesRead === 0) break;
|
|
994
|
-
const b = buf[0] as number;
|
|
995
|
-
if (b === 0x0a) break; // newline terminates the line (stripped, like input()).
|
|
996
|
-
chars.push(b);
|
|
997
|
-
}
|
|
998
|
-
return Buffer.from(chars).toString('utf-8');
|
|
999
|
-
} finally {
|
|
1000
|
-
fs.closeSync(fd);
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
|
-
|
|
1004
943
|
export interface ConfirmVerdict {
|
|
1005
944
|
proceed: boolean;
|
|
1006
945
|
/** Message to surface when not proceeding (no trailing newline). */
|
|
@@ -1024,7 +963,7 @@ export function confirmGate(target: string, yes: boolean): ConfirmVerdict {
|
|
|
1024
963
|
'No terminal available for the [y/N] confirmation (non-interactive shell). ' +
|
|
1025
964
|
'Re-run with --yes to confirm, e.g. `task release -- --yes`.',
|
|
1026
965
|
};
|
|
1027
|
-
if (!
|
|
966
|
+
if (!canPrompt()) return noTerminal;
|
|
1028
967
|
const answer = confirm(`Proceed with release ${target}?`);
|
|
1029
968
|
// null = the controlling terminal vanished between the probe and the read;
|
|
1030
969
|
// surface the actionable --yes guidance, never a bare silent "aborted.".
|
|
@@ -1684,17 +1623,33 @@ function main(argv: readonly string[] | null = null): number {
|
|
|
1684
1623
|
|
|
1685
1624
|
const bump = resolve_bump(args.bump_override, commits);
|
|
1686
1625
|
|
|
1687
|
-
//
|
|
1688
|
-
//
|
|
1689
|
-
//
|
|
1690
|
-
|
|
1626
|
+
// An in-flight release is a STATE, never a flag — so the probe runs
|
|
1627
|
+
// unconditionally and `--resume` no longer decides what is being released.
|
|
1628
|
+
//
|
|
1629
|
+
// The comment this replaces already stated the right intent ("so we don't
|
|
1630
|
+
// accidentally start a 1.16.0 release while 1.15.0 is still in flight") and
|
|
1631
|
+
// the `args.resume ?` gate narrowed it to resumed runs only. Measured on
|
|
1632
|
+
// 14.20.0: `guard_release_curation` refuses after step 2 has bumped
|
|
1633
|
+
// `package.json`, so a plain re-run computed the bump from the ALREADY
|
|
1634
|
+
// BUMPED version, landed on 14.21.0, and then refused its own branch with
|
|
1635
|
+
// `release must run from 'main' or 'release/14.21.0', currently on
|
|
1636
|
+
// 'release/14.20.0'`. The 2026-09-07 start-position fix could not help: it
|
|
1637
|
+
// was asked about the wrong target.
|
|
1638
|
+
//
|
|
1639
|
+
// Neither branch of `_detect_in_flight_target` needs the flag — HEAD
|
|
1640
|
+
// sitting on `release/X.Y.Z`, and a `package.json` version whose tag is not
|
|
1641
|
+
// PUBLISHED, are both facts about the repository. On a clean `main` after a
|
|
1642
|
+
// completed release the second branch returns null (the tag is published),
|
|
1643
|
+
// so a fresh release is unaffected; what changes is that an abandoned bump
|
|
1644
|
+
// can no longer be silently released as the NEXT version.
|
|
1645
|
+
const in_flight = _detect_in_flight_target();
|
|
1691
1646
|
let target: string;
|
|
1692
1647
|
if (args.explicit) {
|
|
1693
1648
|
target = args.explicit;
|
|
1694
1649
|
} else if (in_flight) {
|
|
1695
1650
|
target = in_flight;
|
|
1696
1651
|
process.stdout.write(
|
|
1697
|
-
`
|
|
1652
|
+
`in-flight target ${in_flight} (package.json version with no published tag yet)\n`,
|
|
1698
1653
|
);
|
|
1699
1654
|
} else {
|
|
1700
1655
|
target = bump_version(current, bump);
|
|
@@ -244,17 +244,13 @@ export function defaultChangelogFixture(target: string): string {
|
|
|
244
244
|
'',
|
|
245
245
|
'- fixture entry for the release drill',
|
|
246
246
|
'',
|
|
247
|
-
//
|
|
248
|
-
//
|
|
249
|
-
//
|
|
250
|
-
//
|
|
251
|
-
//
|
|
252
|
-
//
|
|
253
|
-
// than measured: measuring would couple every scenario to the live
|
|
254
|
-
// repository's commit span, which is the coupling `changelog` was
|
|
255
|
-
// introduced to remove.
|
|
247
|
+
// The mix line is written OUT rather than measured: measuring would
|
|
248
|
+
// couple every scenario to the live repository's commit span, which is
|
|
249
|
+
// the coupling `changelog` was introduced to remove. It is no longer a
|
|
250
|
+
// publication obligation (ADR-261 deleted the written answer it used to
|
|
251
|
+
// carry) — it stays because the drill exercises the shape the writer
|
|
252
|
+
// actually renders.
|
|
256
253
|
'> **Governance mix:** governance-only 2 vs consumer-only 1 (taxonomy 1.0.0).',
|
|
257
|
-
'> Next cycle ships the fixture consumer work, tracked in the drill.',
|
|
258
254
|
'',
|
|
259
255
|
'Tests: 1000 (+0 since 0.0.0)',
|
|
260
256
|
'',
|
|
@@ -124,6 +124,7 @@ function _set_changelog_reader(fn: ((file: string) => string) | null): void {
|
|
|
124
124
|
function read_changelog_text(): string {
|
|
125
125
|
return _changelog_reader ? _changelog_reader(CHANGELOG) : fs.readFileSync(CHANGELOG, 'utf-8');
|
|
126
126
|
}
|
|
127
|
+
|
|
127
128
|
const MAIN_BRANCH = 'main';
|
|
128
129
|
const REMOTE = 'origin';
|
|
129
130
|
const REPO_SLUG = 'event4u-app/agent-config';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"taxonomy_version": "1.
|
|
3
|
-
"updated": "2026-09-
|
|
2
|
+
"taxonomy_version": "1.1.0",
|
|
3
|
+
"updated": "2026-09-08",
|
|
4
4
|
"contract": "docs/decisions/ADR-253-per-pr-user-artifact-gate-declined.md",
|
|
5
5
|
"reader": "src/scripts/measure_release_mix.ts",
|
|
6
6
|
"note": [
|
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
{ "prefix": "src/install/", "category": "consumer" },
|
|
63
63
|
{ "prefix": "src/server/", "category": "consumer" },
|
|
64
64
|
{ "prefix": "src/ui/", "category": "consumer" },
|
|
65
|
+
{ "prefix": "src/vendor/grammars/", "category": "consumer" },
|
|
65
66
|
{ "prefix": "src/shared/", "category": "unclassified" },
|
|
66
67
|
|
|
67
68
|
{ "prefix": "src/scripts/", "category": "maintenance" },
|
|
@@ -458,15 +458,14 @@ export function local_release_gate_argv(): string[] {
|
|
|
458
458
|
/**
|
|
459
459
|
* Measure the governance-versus-product level for the span under release.
|
|
460
460
|
*
|
|
461
|
-
*
|
|
462
|
-
*
|
|
463
|
-
*
|
|
464
|
-
*
|
|
465
|
-
* 14.17.0 red after a branch, a PR and a CI run had already been spent.
|
|
461
|
+
* REPORTING ONLY since ADR-261. It feeds exactly one consumer: the `>
|
|
462
|
+
* **Governance mix:** …` line the writer renders into the section. No guard
|
|
463
|
+
* reads it, nothing refuses over it, and `triggered` gates nothing — the
|
|
464
|
+
* written-answer obligation it used to serve is deleted.
|
|
466
465
|
*
|
|
467
466
|
* Returns `null` on any measurement failure — a shallow clone or a missing tag
|
|
468
|
-
* is an environment fact, and
|
|
469
|
-
*
|
|
467
|
+
* is an environment fact, and a release must not stop over one. `null` means
|
|
468
|
+
* the line is simply not rendered.
|
|
470
469
|
*/
|
|
471
470
|
export function measure_mix_obligation(
|
|
472
471
|
version: string,
|
|
@@ -478,19 +477,18 @@ export function measure_mix_obligation(
|
|
|
478
477
|
const reading = measureRange(from, 'HEAD', loadTaxonomy(), version, REPO_ROOT);
|
|
479
478
|
const o = reading.response_obligation;
|
|
480
479
|
return {
|
|
481
|
-
triggered: o.triggered,
|
|
482
480
|
level:
|
|
483
481
|
`governance-only ${String(o.governance_only)} vs consumer-only ` +
|
|
484
482
|
`${String(o.consumer_only)} (taxonomy ${reading.taxonomy_version})`,
|
|
485
483
|
};
|
|
486
484
|
} catch (err) {
|
|
487
|
-
//
|
|
488
|
-
//
|
|
489
|
-
//
|
|
490
|
-
//
|
|
485
|
+
// Printed rather than swallowed. Nothing refuses over the reading any
|
|
486
|
+
// more (ADR-261), so this is not a blocked release — it is a section
|
|
487
|
+
// that will publish without its `> **Governance mix:** …` line, and the
|
|
488
|
+
// operator is entitled to know why the line is missing.
|
|
491
489
|
process.stderr.write(
|
|
492
490
|
`⚠️ governance mix not measured for ${version}: ${(err as Error).message}\n` +
|
|
493
|
-
'
|
|
491
|
+
' The section will publish without the measured line. Nothing is blocked.\n',
|
|
494
492
|
);
|
|
495
493
|
return null;
|
|
496
494
|
}
|
|
@@ -539,12 +537,7 @@ export function guard_release_branch_push(branch: string): void {
|
|
|
539
537
|
if (!section) {
|
|
540
538
|
return;
|
|
541
539
|
}
|
|
542
|
-
const blockers = section_publication_blockers(
|
|
543
|
-
section.body,
|
|
544
|
-
version,
|
|
545
|
-
`\`${branch}\``,
|
|
546
|
-
measure_mix_obligation(version),
|
|
547
|
-
);
|
|
540
|
+
const blockers = section_publication_blockers(section.body, version, `\`${branch}\``);
|
|
548
541
|
if (blockers.length === 0) {
|
|
549
542
|
return;
|
|
550
543
|
}
|
|
@@ -685,6 +678,13 @@ export function integrate_main_if_behind(branch: string, say: (msg: string) => v
|
|
|
685
678
|
run(['git', 'merge', `${REMOTE}/${MAIN_BRANCH}`, '--no-edit']);
|
|
686
679
|
}
|
|
687
680
|
|
|
681
|
+
/** Everything this site refuses over: the head-level blockers, nothing else. */
|
|
682
|
+
function curation_blockers(version: string): string[] {
|
|
683
|
+
const section = extract_changelog_section(read_changelog_text(), version);
|
|
684
|
+
if (section === null) return [];
|
|
685
|
+
return publication_blockers(section.body, version, `\`release/${version}\``);
|
|
686
|
+
}
|
|
687
|
+
|
|
688
688
|
export function guard_release_curation(version: string, prMerged = false): void {
|
|
689
689
|
if (prMerged) {
|
|
690
690
|
return;
|
|
@@ -693,8 +693,7 @@ export function guard_release_curation(version: string, prMerged = false): void
|
|
|
693
693
|
if (!section) {
|
|
694
694
|
return;
|
|
695
695
|
}
|
|
696
|
-
// The head-level blockers
|
|
697
|
-
// tests footer.
|
|
696
|
+
// The head-level blockers only — deliberately NOT the tests footer.
|
|
698
697
|
//
|
|
699
698
|
// This site runs BEFORE `prepend_changelog`'s output is committed, and
|
|
700
699
|
// `--resume` skips the whole bump block once `package.json` already carries
|
|
@@ -707,17 +706,23 @@ export function guard_release_curation(version: string, prMerged = false): void
|
|
|
707
706
|
//
|
|
708
707
|
// `guard_release_branch_push` carries it instead: by then the section is
|
|
709
708
|
// committed and editable on the branch, and nothing has left the machine.
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
709
|
+
//
|
|
710
|
+
// What is NOT here any more (ADR-261): the governance-versus-product
|
|
711
|
+
// written answer, the next-cycle promise, the read-back of the previous
|
|
712
|
+
// one, the `## [Unreleased]` staging channel that fed them and the
|
|
713
|
+
// interactive prompt that asked for them. This guard refuses the
|
|
714
|
+
// generator's own unrewritten draft — a claim no human has looked at — and
|
|
715
|
+
// nothing else. It never asks the operator to write prose about a future
|
|
716
|
+
// cycle to get a version out.
|
|
717
|
+
const blockers = curation_blockers(version);
|
|
718
718
|
if (blockers.length === 0) {
|
|
719
719
|
return;
|
|
720
720
|
}
|
|
721
|
+
// Read once. This used to re-read the file here, because
|
|
722
|
+
// `consume_staged_answers` / `collect_curation_answers` could rewrite the
|
|
723
|
+
// section between the two reads; both write paths are gone (ADR-261) and
|
|
724
|
+
// `curation_blockers` is read-only, so a second read could only ever
|
|
725
|
+
// return the same bytes.
|
|
721
726
|
const marked = section.body
|
|
722
727
|
.split('\n')
|
|
723
728
|
.filter((l) => l.includes(DERIVED_MARKER))
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* The per-host injection-effect census — one line per host, provenance or none.
|
|
4
|
+
*
|
|
5
|
+
* `road-to-delivery-on-hook-hosts` steps 1.2 and 1.3. It renders the committed
|
|
6
|
+
* record at `src/config/host-injection-effect.json` and adds the one thing the
|
|
7
|
+
* record deliberately does not hold: the COMPUTED half, read fresh from the hook
|
|
8
|
+
* manifest, so a reader sees both axes side by side and can see that they
|
|
9
|
+
* disagree.
|
|
10
|
+
*
|
|
11
|
+
* WHY BOTH COLUMNS ARE PRINTED, ALWAYS
|
|
12
|
+
* ------------------------------------
|
|
13
|
+
* `cowork` is the row that makes this table worth generating: eight slots bound
|
|
14
|
+
* and a measured `observed-false`. Printing only the observation would hide the
|
|
15
|
+
* near-miss; printing only the binding would produce the wrong verdict for the
|
|
16
|
+
* one host we have actually measured. The table exists so the gap between "could
|
|
17
|
+
* emit" and "did arrive" is visible per host rather than argued in prose.
|
|
18
|
+
*
|
|
19
|
+
* It computes NO verdict. `admissibleUnderE3` is a one-line read of the observed
|
|
20
|
+
* column and is applied by the caller that would change a projection, never here:
|
|
21
|
+
* a report that also decided admission would be the single place where an
|
|
22
|
+
* inference could quietly replace an observation.
|
|
23
|
+
*
|
|
24
|
+
* Usage:
|
|
25
|
+
* ./scripts-run src/scripts/report_host_injection_effect
|
|
26
|
+
* ./scripts-run src/scripts/report_host_injection_effect --emit --pin <sha>
|
|
27
|
+
*
|
|
28
|
+
* Deterministic: hosts sort by id, no timestamp, the pin is an argument.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import { execFileSync, spawnSync } from 'node:child_process';
|
|
32
|
+
import * as fs from 'node:fs';
|
|
33
|
+
import * as path from 'node:path';
|
|
34
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
35
|
+
|
|
36
|
+
import {
|
|
37
|
+
concernBound,
|
|
38
|
+
loadRecord,
|
|
39
|
+
recordProblems,
|
|
40
|
+
type InjectionRecord,
|
|
41
|
+
} from './_lib/injection_effect.js';
|
|
42
|
+
|
|
43
|
+
const _HERE = fileURLToPath(import.meta.url);
|
|
44
|
+
export const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
|
|
45
|
+
export const ARTIFACT_REL = path.join(
|
|
46
|
+
'agents', 'evidence', 'analysis', 'host-injection-effect-2026-09.md',
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
/** Platform → slot → concern ids, straight out of the manifest. */
|
|
50
|
+
export function loadPlatforms(root: string): Record<string, Record<string, string[]>> {
|
|
51
|
+
const raw = fs.readFileSync(path.join(root, 'src', 'scripts', 'hook_manifest.yaml'), 'utf-8');
|
|
52
|
+
const out = spawnSync(
|
|
53
|
+
process.execPath,
|
|
54
|
+
[
|
|
55
|
+
'-e',
|
|
56
|
+
'const y=require("js-yaml");let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{' +
|
|
57
|
+
'const d=y.load(s);process.stdout.write(JSON.stringify(d.platforms||{}))})',
|
|
58
|
+
],
|
|
59
|
+
{ input: raw, encoding: 'utf-8', cwd: root, maxBuffer: 64 * 1024 * 1024 },
|
|
60
|
+
);
|
|
61
|
+
if (out.status !== 0 || !out.stdout) {
|
|
62
|
+
throw new Error(`hook_manifest.yaml could not be parsed: ${out.stderr || 'no output'}`);
|
|
63
|
+
}
|
|
64
|
+
return JSON.parse(out.stdout) as Record<string, Record<string, string[]>>;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Keys under a platform that are NOT lifecycle slots.
|
|
69
|
+
*
|
|
70
|
+
* `fallback_only` is a marker saying this package binds nothing here, and
|
|
71
|
+
* counting it as a slot made copilot read 1 against the 0 that
|
|
72
|
+
* `docs/enforcement-by-host.md` records — a one-row disagreement with the host
|
|
73
|
+
* table for a nameable reason, which is the drift this repository gates
|
|
74
|
+
* elsewhere. Caught by comparing the generated table against that doc rather
|
|
75
|
+
* than by a test, so it is named here.
|
|
76
|
+
*/
|
|
77
|
+
const NON_SLOT_KEYS: ReadonlySet<string> = new Set(['fallback_only']);
|
|
78
|
+
|
|
79
|
+
/** Lifecycle slots bound for a host, on the host table's own definition. */
|
|
80
|
+
export function boundSlotCount(platforms: Readonly<Record<string, Readonly<Record<string, readonly string[]>>>>, host: string): number {
|
|
81
|
+
return Object.keys(platforms[host] ?? {}).filter((k) => !NON_SLOT_KEYS.has(k)).length;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Host ids that are real rows, in sort order. Underscore keys are annotations. */
|
|
85
|
+
export function hostIds(rec: InjectionRecord): string[] {
|
|
86
|
+
return Object.keys(rec).filter((k) => !k.startsWith('_')).sort();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function renderArtifact(root: string, pin: string): string {
|
|
90
|
+
const rec = loadRecord(root);
|
|
91
|
+
const platforms = loadPlatforms(root);
|
|
92
|
+
const L: string[] = [];
|
|
93
|
+
L.push('<!-- evidence-type: analysis -->');
|
|
94
|
+
L.push('# Host injection effect — does a delivered rule body reach the model?');
|
|
95
|
+
L.push('');
|
|
96
|
+
L.push(`Pinned to commit \`${pin}\`. Generated by`);
|
|
97
|
+
L.push(`\`./scripts-run src/scripts/report_host_injection_effect --emit --pin ${pin}\`.`);
|
|
98
|
+
L.push('Observations come from `src/config/host-injection-effect.json`; the binding column is');
|
|
99
|
+
L.push('read fresh from `src/scripts/hook_manifest.yaml` on every run.');
|
|
100
|
+
L.push('');
|
|
101
|
+
L.push('## The two columns, and why neither is the other');
|
|
102
|
+
L.push('');
|
|
103
|
+
L.push('**Slots bound** is computed and cheap. **Body reaches the model** is observed and');
|
|
104
|
+
L.push('expensive, and owner ruling E3 admits a host to `lean_projection.hosts` on the second');
|
|
105
|
+
L.push('only: one transcript in which a model visibly acts on a delivered body. Documentation');
|
|
106
|
+
L.push('of a host\'s hook API is not admission.');
|
|
107
|
+
L.push('');
|
|
108
|
+
L.push('`cowork` is why the distinction is not pedantry: **eight slots bound, and measured');
|
|
109
|
+
L.push('`observed-false`** — its trampoline discards dispatcher output and exits 0. A table');
|
|
110
|
+
L.push('with only the binding column would admit it.');
|
|
111
|
+
L.push('');
|
|
112
|
+
L.push('## Per host');
|
|
113
|
+
L.push('');
|
|
114
|
+
L.push('| Host | Slots bound | `rule-inject` on `user_prompt_submit` | Body reaches the model | Citation |');
|
|
115
|
+
L.push('|---|---:|:-:|---|---|');
|
|
116
|
+
for (const host of hostIds(rec)) {
|
|
117
|
+
const obs = rec[host];
|
|
118
|
+
const slots = boundSlotCount(platforms, host);
|
|
119
|
+
const bound = concernBound(platforms, host, 'user_prompt_submit', 'rule-inject');
|
|
120
|
+
const cite =
|
|
121
|
+
obs !== undefined && obs.transcript !== null && obs.date !== null
|
|
122
|
+
? `\`${obs.transcript}\` (${obs.date})`
|
|
123
|
+
: '—';
|
|
124
|
+
L.push(
|
|
125
|
+
`| \`${host}\` | ${String(slots)} | ${bound ? 'yes' : 'no'} | \`${obs?.state ?? 'unobserved'}\` | ${cite} |`,
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
L.push('');
|
|
129
|
+
L.push('## Why each row reads as it does');
|
|
130
|
+
L.push('');
|
|
131
|
+
for (const host of hostIds(rec)) {
|
|
132
|
+
L.push(`- **\`${host}\`** — ${rec[host]?.reason ?? 'no row'}`);
|
|
133
|
+
}
|
|
134
|
+
const mdTree = rec['_cursor_md_tree_loaded'];
|
|
135
|
+
if (mdTree !== undefined) {
|
|
136
|
+
L.push('');
|
|
137
|
+
L.push('## `cursor.md_tree_loaded` (step 1.3)');
|
|
138
|
+
L.push('');
|
|
139
|
+
L.push(`\`cursor.md_tree_loaded: ${mdTree.state}\` — ${mdTree.reason}`);
|
|
140
|
+
}
|
|
141
|
+
L.push('');
|
|
142
|
+
L.push('## What this census does NOT do');
|
|
143
|
+
L.push('');
|
|
144
|
+
L.push('It computes no verdict. Admission under E3 is a one-line read of the observed column');
|
|
145
|
+
L.push('and is applied by whatever would change a projection, never here — a report that also');
|
|
146
|
+
L.push('decided admission would be the one place an inference could quietly replace an');
|
|
147
|
+
L.push('observation.');
|
|
148
|
+
L.push('');
|
|
149
|
+
L.push('`unobserved` is a RESULT, not a gap: the host keeps its full rule bodies and loses');
|
|
150
|
+
L.push('nothing while it waits, which is the roadmap\'s K6 and is asserted per PR by');
|
|
151
|
+
L.push('`check_host_tree_parity` in the predecessor.');
|
|
152
|
+
L.push('');
|
|
153
|
+
return L.join('\n');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function headSha(root: string): string {
|
|
157
|
+
return execFileSync('git', ['-C', root, 'rev-parse', 'HEAD'], { encoding: 'utf-8' }).trim();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function main(argv: readonly string[]): number {
|
|
161
|
+
const root = REPO_ROOT;
|
|
162
|
+
const rec = loadRecord(root);
|
|
163
|
+
const problems = recordProblems(rec);
|
|
164
|
+
if (problems.length > 0) {
|
|
165
|
+
process.stderr.write('❌ the observation record is not admissible:\n');
|
|
166
|
+
for (const p of problems) process.stderr.write(` · ${p.host}: ${p.message}\n`);
|
|
167
|
+
return 1;
|
|
168
|
+
}
|
|
169
|
+
const pinIdx = argv.indexOf('--pin');
|
|
170
|
+
const pin = pinIdx !== -1 && pinIdx + 1 < argv.length ? (argv[pinIdx + 1] as string) : headSha(root);
|
|
171
|
+
if (argv.includes('--emit')) {
|
|
172
|
+
const out = path.join(root, ARTIFACT_REL);
|
|
173
|
+
fs.mkdirSync(path.dirname(out), { recursive: true });
|
|
174
|
+
fs.writeFileSync(out, renderArtifact(root, pin), 'utf-8');
|
|
175
|
+
process.stdout.write(`wrote ${ARTIFACT_REL} · pin ${pin}\n`);
|
|
176
|
+
}
|
|
177
|
+
const hosts = hostIds(rec);
|
|
178
|
+
const byState = new Map<string, number>();
|
|
179
|
+
for (const h of hosts) byState.set(rec[h]!.state, (byState.get(rec[h]!.state) ?? 0) + 1);
|
|
180
|
+
process.stdout.write(`scanned: ${String(hosts.length)} host(s)\n`);
|
|
181
|
+
for (const [state, n] of [...byState].sort()) {
|
|
182
|
+
process.stdout.write(` ${state.padEnd(16)} ${String(n)}\n`);
|
|
183
|
+
}
|
|
184
|
+
return 0;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (process.argv[1] !== undefined && import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href) {
|
|
188
|
+
process.exit(main(process.argv.slice(2)));
|
|
189
|
+
}
|
|
@@ -24,6 +24,7 @@ import * as path from 'node:path';
|
|
|
24
24
|
import process from 'node:process';
|
|
25
25
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
26
26
|
|
|
27
|
+
import { resolveHostLayerVerdict, setPartitionAnnounce } from '../install/partitionEligibility.js';
|
|
27
28
|
import { warnLayerOverlap } from './_lib/layer_overlap_notice.js';
|
|
28
29
|
|
|
29
30
|
// ledger-exempt: this is a REPORTER, not a gate — it always exits 0 by construction
|
|
@@ -59,6 +60,46 @@ export function main(argv?: readonly string[]): number {
|
|
|
59
60
|
return 1;
|
|
60
61
|
}
|
|
61
62
|
}
|
|
63
|
+
// The projection line, and where it lives is a correction. **Both council seats
|
|
64
|
+
// (2026-08-20, 2/2) required that generation PRINT the mode it selected** rather
|
|
65
|
+
// than partition silently. The 2026-09-07 change moved the withhold off
|
|
66
|
+
// `installed.lock` and, in doing so, removed `condense.ts`'s only call into the
|
|
67
|
+
// resolver — so the emitter it installed became dead and generation withheld
|
|
68
|
+
// ~299 skills and 29 personas while printing nothing about the layer it
|
|
69
|
+
// withheld against. A neutral review found it the same day.
|
|
70
|
+
//
|
|
71
|
+
// It prints from HERE rather than from `condense.ts` because that file is ~1,200
|
|
72
|
+
// lines past the source-size ceiling, where the ratchet counts every added line
|
|
73
|
+
// and its test blocks on `baseline == live`. This step already runs in the same
|
|
74
|
+
// `generate-tools` chain, one line below the generator, so the operator sees the
|
|
75
|
+
// two lines together.
|
|
76
|
+
// Silence the resolver's OWN one-liner first: it writes to stdout by default,
|
|
77
|
+
// and leaving it on printed the verdict twice — once bare, once with the
|
|
78
|
+
// remediation text below. One line, the useful one.
|
|
79
|
+
setPartitionAnnounce(() => undefined);
|
|
80
|
+
const verdict = resolveHostLayerVerdict(projectRoot);
|
|
81
|
+
// Three states, not two. Corrected after a second neutral review: the
|
|
82
|
+
// unverified branch printed "a stale global layer is fixed by
|
|
83
|
+
// `agent-config install`" on a machine with NO global layer at all — a fresh
|
|
84
|
+
// checkout, where nothing is withheld, nothing is stale, and the remediation
|
|
85
|
+
// named does not apply. It fired on every `task generate-tools` there.
|
|
86
|
+
//
|
|
87
|
+
// What this line reports is the VERIFICATION state of the layer withheld
|
|
88
|
+
// against. The per-artefact withhold COUNTS are the generator's own summary
|
|
89
|
+
// one line above (`skills=N`, `command_skills=N (M withheld …)`), which is
|
|
90
|
+
// where a reader sees what was actually held back.
|
|
91
|
+
const noLayer = verdict.reason.includes('no host-global layer');
|
|
92
|
+
process.stdout.write(
|
|
93
|
+
verdict.verified
|
|
94
|
+
? ` ℹ️ project layer carries only what ~/.claude lacks — ${verdict.reason}\n`
|
|
95
|
+
: noLayer
|
|
96
|
+
? ' ℹ️ no host-global layer on this machine — nothing is withheld, so the\n' +
|
|
97
|
+
' project layer carries the full projection by construction.\n'
|
|
98
|
+
: ` ⚠️ host layer UNVERIFIED — ${verdict.reason}\n` +
|
|
99
|
+
' The project layer still carries only what ~/.claude lacks (per-artefact,\n' +
|
|
100
|
+
' ADR-236 amendment 2026-09-07). A stale global layer is fixed by\n' +
|
|
101
|
+
' `agent-config install`, not by re-running generate-tools.\n',
|
|
102
|
+
);
|
|
62
103
|
warnLayerOverlap(projectRoot, (m) => process.stdout.write(`${m}\n`));
|
|
63
104
|
return 0;
|
|
64
105
|
}
|