@event4u/agent-config 14.7.0 → 14.9.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 +175 -390
- package/README.md +1 -1
- package/agents/templates/.ai-council.yml.example +34 -4
- 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 +383 -0
- package/dist/agent-src/commands/review/changes.md +95 -14
- package/dist/agent-src/commands/roadmap/process-full.md +157 -3
- 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/host-capability-manifest.md +77 -0
- package/dist/agent-src/contexts/execution/roadmap-execution-contract.md +100 -2
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +154 -2
- package/dist/agent-src/contexts/execution/subagent-response-contract.md +80 -0
- package/dist/agent-src/rules/artifact-drafting-protocol.md +5 -1
- package/dist/agent-src/rules/decision-revisit-gate.md +65 -33
- package/dist/agent-src/rules/recurring-criticism.md +1 -1
- package/dist/agent-src/rules/roadmap-progress-sync.md +19 -0
- package/dist/agent-src/rules/source-confidentiality.md +1 -1
- package/dist/agent-src/rules/untrusted-input-defense.md +9 -3
- package/dist/agent-src/scripts/archive_completed_roadmaps.ts +7 -4
- package/dist/agent-src/scripts/dashboard_mode.ts +212 -0
- package/dist/agent-src/scripts/update_roadmap_progress.ts +47 -48
- package/dist/agent-src/skills/adr-create/SKILL.md +124 -5
- package/dist/agent-src/skills/code-review/evals/evals.json +87 -46
- 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/humanizer/SKILL.md +39 -0
- package/dist/agent-src/skills/humanizer/evals/strip_fixtures.json +57 -0
- package/dist/agent-src/skills/humanizer/references/fixtures.md +54 -0
- package/dist/agent-src/skills/judge-spec-compliance/SKILL.md +154 -0
- package/dist/agent-src/skills/judge-synthesis/SKILL.md +45 -0
- package/dist/agent-src/skills/merge-conflicts/SKILL.md +26 -0
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +18 -18
- 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/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/roadmaps.md +28 -2
- package/dist/cli/registry.js +3 -0
- package/dist/cli/registry.js.map +1 -1
- package/dist/cli-delegate/{chunk-FBKL6JWT.js → chunk-77GR3VD4.js} +68 -43
- package/dist/cli-delegate/{chunk-JZ4NR2TW.js → chunk-7PRE6ZV2.js} +1 -1
- package/dist/cli-delegate/{chunk-UEZ7D35A.js → chunk-CYH5SP6M.js} +4 -23
- package/dist/cli-delegate/{chunk-Q26HT4QT.js → chunk-E46KPN6K.js} +251 -150
- package/dist/cli-delegate/{chunk-74E3TKWV.js → chunk-H4VIVD4C.js} +5 -5
- package/dist/cli-delegate/chunk-I3EIN2YG.js +36 -0
- package/dist/cli-delegate/{chunk-4VR4ORQ6.js → chunk-LPPWO5CE.js} +4 -3
- package/dist/cli-delegate/{chunk-7F3VYSM2.js → chunk-PLSG6UL5.js} +14 -14
- package/dist/cli-delegate/cmd_adr_effective.js +1162 -0
- package/dist/cli-delegate/cmd_conformance.js +101 -25
- package/dist/cli-delegate/cmd_converge.js +3 -3
- package/dist/cli-delegate/cmd_doctor.js +7 -7
- package/dist/cli-delegate/cmd_export.js +6 -6
- package/dist/cli-delegate/cmd_handoff.js +7 -6
- package/dist/cli-delegate/cmd_memory_get.js +2 -1
- package/dist/cli-delegate/cmd_migrate.js +2 -2
- package/dist/cli-delegate/cmd_preflight.js +5 -5
- package/dist/cli-delegate/cmd_prune.js +6 -6
- 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 +3 -3
- package/dist/cli-delegate/cmd_settings_get.js +4 -4
- package/dist/cli-delegate/cmd_settings_migrate.js +6 -6
- package/dist/cli-delegate/cmd_settings_set.js +1 -1
- package/dist/cli-delegate/cmd_sync.js +6 -6
- package/dist/cli-delegate/cmd_uninstall.js +6 -6
- package/dist/cli-delegate/cmd_update.js +3 -3
- package/dist/cli-delegate/cmd_upgrade.js +3 -3
- package/dist/cli-delegate/cmd_validate.js +6 -6
- package/dist/cli-delegate/cmd_versions.js +3 -3
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +83 -30
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +4 -4
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +11 -9
- package/dist/discovery/trust-report.md +2 -2
- package/dist/discovery/workspaces.json +9 -7
- package/dist/hooks/dispatch.js +1295 -812
- package/dist/install/globalRuleLayers.js +169 -0
- package/dist/install/globalRuleLayers.js.map +1 -0
- package/dist/install/partitionEligibility.js +156 -0
- package/dist/install/partitionEligibility.js.map +1 -1
- 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 +1358 -805
- package/dist/scripts/_lib/claude_plugin.js +162 -0
- package/dist/scripts/_lib/claude_plugin.js.map +1 -0
- package/dist/scripts/_lib/environment_detector.js +397 -0
- package/dist/scripts/_lib/environment_detector.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/_lib/spawn_env.js +129 -0
- package/dist/scripts/_lib/spawn_env.js.map +1 -0
- package/dist/scripts/ai_council/modes.js +199 -0
- package/dist/scripts/ai_council/modes.js.map +1 -0
- package/dist/scripts/ai_council/transport_resolver.js +418 -0
- package/dist/scripts/ai_council/transport_resolver.js.map +1 -0
- package/dist/scripts/condense.js +52 -89
- package/dist/scripts/condense.js.map +1 -1
- package/dist/server/routes/wizard.js +3 -2
- package/dist/server/routes/wizard.js.map +1 -1
- package/dist/ui/assets/index-Cxgy63kz.js +26 -0
- package/dist/ui/assets/index-Cxgy63kz.js.map +1 -0
- package/dist/ui/index.html +1 -1
- package/docs/guidelines/agent-infra/artifact-drafting-protocol-mechanics.md +45 -1
- package/docs/guidelines/agent-infra/false-green.md +47 -1
- package/docs/guidelines/agent-infra/traceability-field-mechanics.md +50 -0
- package/docs/guidelines/agent-infra/untrusted-input-spotlighting.md +17 -0
- package/package.json +7 -3
- package/src/agent-src/scripts/archive_completed_roadmaps.ts +7 -4
- package/src/agent-src/scripts/dashboard_mode.ts +212 -0
- package/src/agent-src/scripts/update_roadmap_progress.ts +47 -48
- package/src/config/ci-local-parity.yml +21 -0
- package/src/config/estate-count-budget.json +13 -471
- package/src/config/evaluator-budgets.json +4 -4
- package/src/config/gate-coverage.yml +458 -12
- package/src/config/gate-violation-baselines.json +18 -8
- package/src/config/gitignore-block.txt +15 -2
- package/src/config/hook-latency-budget.json +5 -0
- package/src/config/publish-surface.json +60 -0
- package/src/config/requirements-trace-budget.json +32 -0
- package/src/config/rule-stub-ceilings.json +25 -4
- package/src/install/globalRuleLayers.ts +197 -0
- package/src/install/partitionEligibility.ts +177 -0
- package/src/install/ruleLayerPartition.ts +212 -0
- package/src/scripts/_cli/cmd_adr_effective.ts +484 -0
- package/src/scripts/_dispatch.bash +18 -0
- package/src/scripts/_lib/adr_frontmatter.test.ts +365 -0
- package/src/scripts/_lib/adr_frontmatter.ts +459 -0
- package/src/scripts/_lib/base_tree.ts +175 -0
- package/src/scripts/_lib/billing_grant.ts +165 -0
- package/src/scripts/_lib/billing_grant_cli.ts +123 -0
- package/src/scripts/_lib/council_fallback_posture.ts +53 -6
- package/src/scripts/_lib/council_fallback_wiring.ts +61 -1
- package/src/scripts/_lib/envelope_grounding.ts +11 -1
- package/src/scripts/_lib/host_capability.ts +59 -5
- package/src/scripts/_lib/layer_overlap_notice.ts +11 -2
- package/src/scripts/_lib/prune_empty_dirs.ts +80 -0
- package/src/scripts/_lib/quota_parked.ts +133 -0
- package/src/scripts/_lib/retrieval_sanitize.ts +62 -0
- package/src/scripts/_lib/review_telemetry.ts +196 -0
- package/src/scripts/_lib/subagent_response.ts +92 -0
- package/src/scripts/adr/evidence_census.ts +899 -0
- package/src/scripts/adr/regenerate_index.ts +25 -38
- package/src/scripts/adr_cite_check.ts +540 -39
- package/src/scripts/ai_council/clients.ts +12 -1
- package/src/scripts/ai_council/config.ts +72 -115
- package/src/scripts/ai_council/fallback_config.ts +19 -7
- package/src/scripts/ai_council/mid_flight_fallback.ts +88 -7
- package/src/scripts/ai_council/orchestrator.ts +2 -2
- package/src/scripts/ai_council/py_format.ts +124 -0
- package/src/scripts/ai_council/transport_resolver.ts +65 -2
- package/src/scripts/audit_adr_coverage.ts +134 -21
- 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_council_pin_staleness.ts +300 -0
- package/src/scripts/check_estate_count.ts +420 -293
- package/src/scripts/check_gate_coverage.ts +91 -0
- package/src/scripts/check_hook_bundle_content.ts +219 -0
- package/src/scripts/check_new_adr_evidence.ts +529 -0
- package/src/scripts/check_no_stub_inventory_table.ts +292 -0
- package/src/scripts/check_pack_size.ts +234 -1
- package/src/scripts/check_publish_surface.ts +297 -0
- package/src/scripts/check_references.ts +18 -0
- package/src/scripts/check_requirements_trace.ts +378 -0
- package/src/scripts/check_rule_layer_partition.ts +531 -0
- package/src/scripts/check_secret_leak.ts +166 -12
- package/src/scripts/check_single_delivery.ts +137 -4
- package/src/scripts/check_standing_rule_delivery.ts +8 -2
- package/src/scripts/code_graph/cli.ts +44 -5
- package/src/scripts/code_graph/extract.ts +87 -0
- package/src/scripts/condense.ts +61 -91
- package/src/scripts/council_cli.ts +12 -16
- package/src/scripts/detect_ai_tells.ts +87 -0
- package/src/scripts/dispatch_r2_reviewer.ts +42 -0
- package/src/scripts/external_sources_denylist.json +4 -1
- package/src/scripts/generate_capabilities_index.ts +41 -0
- package/src/scripts/git_authorization_hook.ts +54 -4
- package/src/scripts/hooks/block_unauthorized_git.ts +41 -3
- 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/run_continuation_hook.ts +3 -1
- package/src/scripts/hooks/state_io.ts +88 -1
- package/src/scripts/hooks/stdin_failure_policy.ts +96 -0
- package/src/scripts/hooks/subagent_ledger_hook.ts +76 -11
- package/src/scripts/injection_scan_hook.ts +257 -15
- package/src/scripts/lint_adr_sweep_routing.ts +365 -0
- package/src/scripts/lint_hook_manifest.ts +49 -0
- package/src/scripts/lint_provenance_vocabulary.ts +472 -4
- package/src/scripts/lint_workflow_security.ts +53 -10
- package/src/scripts/lint_workflow_security_allowlist.json +2 -18
- package/src/scripts/report_envelope_rate.ts +166 -0
- package/src/scripts/review_axis_report.ts +218 -0
- package/src/scripts/routing_doctor.ts +5 -1
- package/src/scripts/run_supervise.ts +46 -1
- package/src/scripts/schemas/evals.schema.json +5 -0
- package/src/scripts/sync_pr_branch.ts +170 -20
- package/dist/cli/commands/commands.candidates.test.js +0 -257
- package/dist/cli/commands/commands.candidates.test.js.map +0 -1
- package/dist/cli/commands/profiles.test.js +0 -88
- package/dist/cli/commands/profiles.test.js.map +0 -1
- package/dist/cli/commands/recordTriggerEval.test.js +0 -113
- package/dist/cli/commands/recordTriggerEval.test.js.map +0 -1
- package/dist/cli/configRoot.test.js +0 -97
- package/dist/cli/configRoot.test.js.map +0 -1
- package/dist/cli/mcp/content.test.js +0 -54
- package/dist/cli/mcp/content.test.js.map +0 -1
- package/dist/cli/mcp/dispatch.test.js +0 -108
- package/dist/cli/mcp/dispatch.test.js.map +0 -1
- package/dist/server/writeRoot.test.js +0 -91
- package/dist/server/writeRoot.test.js.map +0 -1
- package/dist/shared/capabilities.test.js +0 -39
- package/dist/shared/capabilities.test.js.map +0 -1
- package/dist/ui/assets/index-C1pBRm1f.js +0 -26
- package/dist/ui/assets/index-C1pBRm1f.js.map +0 -1
- package/dist/cli-delegate/{chunk-PPQO4NGR.js → chunk-6RJA2F5A.js} +3 -3
- package/dist/cli-delegate/{chunk-6EOCNQAM.js → chunk-E4XIZDIB.js} +0 -0
- package/dist/cli-delegate/{chunk-GJ5BVHXM.js → chunk-PMEKGDED.js} +3 -3
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Make a change to what ships a diff a reviewer reads.
|
|
4
|
+
*
|
|
5
|
+
* THE DEFECT. Two surfaces decide the published payload — `package.json`
|
|
6
|
+
* `files[]` and `.npmignore` — and nothing compared them or noticed when one
|
|
7
|
+
* changed. Editing what ships is legitimate; editing it invisibly is not, and a
|
|
8
|
+
* 26-root allowlist plus a 27-line ignore file is exactly the shape nobody
|
|
9
|
+
* re-reads.
|
|
10
|
+
*
|
|
11
|
+
* THE ARTEFACT IS NOT A COPY OF THE TWO FILES. Copying them would move the
|
|
12
|
+
* problem: a reviewer would still have to know which of the two wins. It
|
|
13
|
+
* records the resolved surface plus the one fact that is genuinely surprising —
|
|
14
|
+
* **`files[]` is an allowlist and it overrides `.npmignore` for anything under
|
|
15
|
+
* an included root.** That was measured, not assumed: an attempt to strip
|
|
16
|
+
* compiled test artefacts via `.npmignore` had NO effect, because `dist/` is an
|
|
17
|
+
* included root; the strip only worked as `!` negations inside `files[]`. So
|
|
18
|
+
* every `.npmignore` pattern is classified against the live payload, and a
|
|
19
|
+
* pattern the payload contradicts is reported SHADOWED rather than listed as
|
|
20
|
+
* though it were doing something.
|
|
21
|
+
*
|
|
22
|
+
* `--write` regenerates. Default checks. The check compares the artefact to the
|
|
23
|
+
* live config, so a stale artefact is the failure rather than the resting state.
|
|
24
|
+
*
|
|
25
|
+
* Exit: 0 in sync, 1 on drift, 2 on a usage error or a dead scope.
|
|
26
|
+
*/
|
|
27
|
+
import { spawnSync } from 'node:child_process';
|
|
28
|
+
import * as fs from 'node:fs';
|
|
29
|
+
import * as path from 'node:path';
|
|
30
|
+
import { fileURLToPath } from 'node:url';
|
|
31
|
+
|
|
32
|
+
import { runSelfTest } from './_lib/gate_self_test.js';
|
|
33
|
+
import { assertScanned, DeadScopeError } from './_lib/scan_scope.js';
|
|
34
|
+
|
|
35
|
+
const _HERE = fileURLToPath(import.meta.url);
|
|
36
|
+
export const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
|
|
37
|
+
export const ARTEFACT = path.join(REPO_ROOT, 'src/config/publish-surface.json');
|
|
38
|
+
|
|
39
|
+
export interface PublishSurface {
|
|
40
|
+
_comment: string;
|
|
41
|
+
/** Positive `files[]` entries — the allowlist roots. */
|
|
42
|
+
roots: string[];
|
|
43
|
+
/** `!`-prefixed `files[]` entries — the only way to remove from a root. */
|
|
44
|
+
negations: string[];
|
|
45
|
+
/** `.npmignore` patterns, comments and blanks dropped. */
|
|
46
|
+
npmignore: string[];
|
|
47
|
+
/**
|
|
48
|
+
* `.npmignore` patterns the live payload contradicts — the rule is present
|
|
49
|
+
* and not doing what its author expected.
|
|
50
|
+
*
|
|
51
|
+
* INFORMATIONAL, and excluded from the drift verdict. It is derived from the
|
|
52
|
+
* live payload, so it is tree-dependent in principle: a built tree ships
|
|
53
|
+
* `dist/cli/**` and an unbuilt one does not. Comparing it would make the
|
|
54
|
+
* gate red for anyone whose build state differs from whoever last
|
|
55
|
+
* regenerated the artefact — the exact false-drift shape the content-class
|
|
56
|
+
* check had to close with `measured_in`.
|
|
57
|
+
*
|
|
58
|
+
* Measured in both states on 2026-08-22 and identical (`['agents/']`), so
|
|
59
|
+
* the tolerance is not covering a known difference; it is refusing to assert
|
|
60
|
+
* one it cannot control.
|
|
61
|
+
*/
|
|
62
|
+
npmignore_shadowed: string[];
|
|
63
|
+
/** Which tree the shadowed set above was measured in. */
|
|
64
|
+
shadowed_measured_in: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Strip comments and blanks; keep author order, because order is reviewable. */
|
|
68
|
+
export function parseNpmignore(text: string): string[] {
|
|
69
|
+
return text
|
|
70
|
+
.split('\n')
|
|
71
|
+
.map((l) => l.replace(/\r$/, '').trim())
|
|
72
|
+
.filter((l) => l !== '' && !l.startsWith('#'));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Does an `.npmignore` pattern match a payload path?
|
|
77
|
+
*
|
|
78
|
+
* Deliberately a narrow subset of gitignore semantics — a leading double-star segment, a
|
|
79
|
+
* trailing `/` for a directory, and `*` inside a segment. A full matcher would
|
|
80
|
+
* be a dependency, and the classification only has to be right about the
|
|
81
|
+
* patterns this file actually uses; a pattern it cannot model is reported as
|
|
82
|
+
* not-shadowed, which is the conservative direction (it never claims a rule is
|
|
83
|
+
* inert without a payload path to prove it).
|
|
84
|
+
*/
|
|
85
|
+
export function npmignoreMatches(pattern: string, payloadPath: string): boolean {
|
|
86
|
+
let p = pattern;
|
|
87
|
+
const anchored = p.startsWith('/');
|
|
88
|
+
if (anchored) p = p.slice(1);
|
|
89
|
+
const dirOnly = p.endsWith('/');
|
|
90
|
+
if (dirOnly) p = p.slice(0, -1);
|
|
91
|
+
const anySegment = p.startsWith('**/');
|
|
92
|
+
if (anySegment) p = p.slice(3);
|
|
93
|
+
const rx = new RegExp(
|
|
94
|
+
`${anchored ? '^' : anySegment ? '(^|/)' : '(^|/)'}${p
|
|
95
|
+
.split('*')
|
|
96
|
+
.map((s) => s.replace(/[.+?^${}()|[\]\\]/g, '\\$&'))
|
|
97
|
+
.join('[^/]*')}${dirOnly ? '/' : '($|/)'}`,
|
|
98
|
+
);
|
|
99
|
+
return rx.test(payloadPath);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function packPayload(root: string): string[] {
|
|
103
|
+
const out = spawnSync('npm', ['pack', '--dry-run', '--ignore-scripts', '--json'], {
|
|
104
|
+
cwd: root,
|
|
105
|
+
encoding: 'utf-8',
|
|
106
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
107
|
+
});
|
|
108
|
+
if (out.status !== 0 || typeof out.stdout !== 'string') return [];
|
|
109
|
+
const i = out.stdout.indexOf('[');
|
|
110
|
+
if (i < 0) return [];
|
|
111
|
+
try {
|
|
112
|
+
const parsed = JSON.parse(out.stdout.slice(i)) as { files?: { path?: unknown }[] }[];
|
|
113
|
+
const files = Array.isArray(parsed[0]?.files) ? (parsed[0]?.files ?? []) : [];
|
|
114
|
+
return files.map((f) => (typeof f.path === 'string' ? f.path : '')).filter((s) => s !== '');
|
|
115
|
+
} catch {
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function buildSurface(root: string, payload: readonly string[]): PublishSurface {
|
|
121
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf-8')) as {
|
|
122
|
+
files?: string[];
|
|
123
|
+
};
|
|
124
|
+
const entries = pkg.files ?? [];
|
|
125
|
+
const ignPath = path.join(root, '.npmignore');
|
|
126
|
+
const npmignore = fs.existsSync(ignPath) ? parseNpmignore(fs.readFileSync(ignPath, 'utf-8')) : [];
|
|
127
|
+
const shadowed = npmignore.filter((pat) => payload.some((p) => npmignoreMatches(pat, p)));
|
|
128
|
+
return {
|
|
129
|
+
_comment:
|
|
130
|
+
'GENERATED by src/scripts/check_publish_surface.ts --write. Do not hand-edit. ' +
|
|
131
|
+
'It records the resolved publish surface so a change to what ships is a diff a reviewer reads. ' +
|
|
132
|
+
'`files[]` is an ALLOWLIST and it overrides .npmignore for anything under an included root — ' +
|
|
133
|
+
'measured, after an attempt to strip compiled test artefacts via .npmignore had no effect because ' +
|
|
134
|
+
'dist/ is an included root. That is why `negations` exists and why `npmignore_shadowed` is reported: ' +
|
|
135
|
+
'a pattern the live payload contradicts is present and not doing what its author expected.',
|
|
136
|
+
roots: entries.filter((e) => !e.startsWith('!')),
|
|
137
|
+
negations: entries.filter((e) => e.startsWith('!')),
|
|
138
|
+
npmignore,
|
|
139
|
+
npmignore_shadowed: shadowed,
|
|
140
|
+
shadowed_measured_in:
|
|
141
|
+
payload.some((p) => p.startsWith('dist/cli/'))
|
|
142
|
+
? 'built tree (payload carries dist/cli/**)'
|
|
143
|
+
: 'unbuilt tree (payload carries no dist/cli/**)',
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* The fields the drift verdict compares — everything read from a FILE, nothing
|
|
149
|
+
* derived from the payload. See `npmignore_shadowed`.
|
|
150
|
+
*/
|
|
151
|
+
export function driftKey(s: PublishSurface): string {
|
|
152
|
+
return `${JSON.stringify({ roots: s.roots, negations: s.negations, npmignore: s.npmignore }, null, 2)}\n`;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function render(s: PublishSurface): string {
|
|
156
|
+
return `${JSON.stringify(s, null, 2)}\n`;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function selfTest(): number {
|
|
160
|
+
return runSelfTest({
|
|
161
|
+
gate: 'check_publish_surface',
|
|
162
|
+
minCases: 7,
|
|
163
|
+
minRejectCases: 4,
|
|
164
|
+
cases: [
|
|
165
|
+
{
|
|
166
|
+
name: 'a directory pattern matches a path under it',
|
|
167
|
+
expect: 'reject',
|
|
168
|
+
run: () => (npmignoreMatches('agents/', 'agents/roadmaps/x.md') ? 1 : 0),
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
name: 'a double-star-prefixed pattern matches at any depth',
|
|
172
|
+
expect: 'reject',
|
|
173
|
+
run: () => (npmignoreMatches('**/*.pyc', 'src/a/b/c.pyc') ? 1 : 0),
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
name: 'a bare filename matches at any depth',
|
|
177
|
+
expect: 'reject',
|
|
178
|
+
run: () => (npmignoreMatches('.DS_Store', 'docs/.DS_Store') ? 1 : 0),
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
name: 'a `*` glob stays within one segment',
|
|
182
|
+
expect: 'reject',
|
|
183
|
+
run: () => (npmignoreMatches('*.swp', 'a.swp') ? 1 : 0),
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
// The counter-test: a segment glob must NOT cross a slash, or
|
|
187
|
+
// every pattern would match everything and the whole
|
|
188
|
+
// shadowed-set would be noise.
|
|
189
|
+
//
|
|
190
|
+
// The first version of this case asserted that `*.swp` does not
|
|
191
|
+
// match `a.swp/b.txt` — which is wrong, and the self-test caught
|
|
192
|
+
// it: gitignore semantics say `*.swp` matches a DIRECTORY of
|
|
193
|
+
// that name and everything under it, so the matcher was right
|
|
194
|
+
// and the fixture was not. `a*b` vs `a/x/b` tests the property
|
|
195
|
+
// the case was reaching for without the ambiguity.
|
|
196
|
+
name: 'a `*` glob does not cross a slash',
|
|
197
|
+
expect: 'accept',
|
|
198
|
+
run: () => (npmignoreMatches('a*b', 'a/x/b') ? 1 : 0),
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
name: 'a directory pattern does not match a same-named file',
|
|
202
|
+
expect: 'accept',
|
|
203
|
+
run: () => (npmignoreMatches('vendor/', 'vendor') ? 1 : 0),
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
name: 'an unrelated pattern matches nothing',
|
|
207
|
+
expect: 'accept',
|
|
208
|
+
run: () => (npmignoreMatches('composer.lock', 'src/scripts/x.ts') ? 1 : 0),
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
// Drift detection itself: the shadowed set must react to the
|
|
212
|
+
// payload, not just to the file. A pattern with no payload
|
|
213
|
+
// match is not shadowed.
|
|
214
|
+
name: 'shadowed is computed against the payload, not asserted',
|
|
215
|
+
expect: 'reject',
|
|
216
|
+
run: () => {
|
|
217
|
+
const withHit = buildSurfaceFrom(['agents/'], ['agents/x.md']);
|
|
218
|
+
const without = buildSurfaceFrom(['agents/'], ['src/x.ts']);
|
|
219
|
+
return withHit.length === 1 && without.length === 0 ? 1 : 0;
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
],
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** Test seam: the shadowed computation alone, with no filesystem. */
|
|
227
|
+
export function buildSurfaceFrom(npmignore: readonly string[], payload: readonly string[]): string[] {
|
|
228
|
+
return npmignore.filter((pat) => payload.some((p) => npmignoreMatches(pat, p)));
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export function main(argv: readonly string[] = process.argv.slice(2)): number {
|
|
232
|
+
if (argv.includes('--self-test')) return selfTest();
|
|
233
|
+
const payload = packPayload(REPO_ROOT);
|
|
234
|
+
try {
|
|
235
|
+
assertScanned({
|
|
236
|
+
gate: 'check_publish_surface',
|
|
237
|
+
scanned: payload.length,
|
|
238
|
+
units: 'packed file(s)',
|
|
239
|
+
roots: ['npm pack --dry-run --ignore-scripts payload (package.json files[])'],
|
|
240
|
+
});
|
|
241
|
+
} catch (err) {
|
|
242
|
+
if (err instanceof DeadScopeError) {
|
|
243
|
+
process.stderr.write(`❌ publish surface: ${err.message}\n`);
|
|
244
|
+
return 2;
|
|
245
|
+
}
|
|
246
|
+
throw err;
|
|
247
|
+
}
|
|
248
|
+
process.stdout.write(`scanned: ${String(payload.length)}\n`);
|
|
249
|
+
const live = render(buildSurface(REPO_ROOT, payload));
|
|
250
|
+
if (argv.includes('--write')) {
|
|
251
|
+
fs.writeFileSync(ARTEFACT, live);
|
|
252
|
+
process.stdout.write(`✅ publish surface written to ${path.relative(REPO_ROOT, ARTEFACT)}\n`);
|
|
253
|
+
return 0;
|
|
254
|
+
}
|
|
255
|
+
if (!fs.existsSync(ARTEFACT)) {
|
|
256
|
+
process.stderr.write(
|
|
257
|
+
`❌ publish surface: ${path.relative(REPO_ROOT, ARTEFACT)} is missing — run with --write\n`,
|
|
258
|
+
);
|
|
259
|
+
return 1;
|
|
260
|
+
}
|
|
261
|
+
const committed = fs.readFileSync(ARTEFACT, 'utf-8');
|
|
262
|
+
let committedKey: string;
|
|
263
|
+
try {
|
|
264
|
+
committedKey = driftKey(JSON.parse(committed) as PublishSurface);
|
|
265
|
+
} catch {
|
|
266
|
+
process.stderr.write(`❌ publish surface: ${path.relative(REPO_ROOT, ARTEFACT)} is not valid JSON\n`);
|
|
267
|
+
return 1;
|
|
268
|
+
}
|
|
269
|
+
if (committedKey === driftKey(buildSurface(REPO_ROOT, payload))) {
|
|
270
|
+
if (committed !== live) {
|
|
271
|
+
// The tree-independent surface matches; only the payload-derived
|
|
272
|
+
// shadowed set differs, which a different build state explains.
|
|
273
|
+
// Said out loud rather than swallowed: a silent difference between
|
|
274
|
+
// the committed artefact and the live one is the thing this gate
|
|
275
|
+
// exists to make visible, even when it is not a failure.
|
|
276
|
+
process.stdout.write(
|
|
277
|
+
'⚠️ publish surface: files[] and .npmignore match, but the payload-derived\n' +
|
|
278
|
+
' shadowed set differs — a different build state, not drift in what ships.\n' +
|
|
279
|
+
' Regenerate with --write if you want the artefact to record THIS tree.\n',
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
process.stdout.write('✅ publish surface in sync with package.json files[] + .npmignore.\n');
|
|
283
|
+
return 0;
|
|
284
|
+
}
|
|
285
|
+
process.stderr.write(
|
|
286
|
+
'❌ publish surface drifted: what ships changed and the committed artefact did not.\n' +
|
|
287
|
+
' Changing what ships is legitimate; changing it without the artefact moving is the defect,\n' +
|
|
288
|
+
' because the change is then invisible in review. Run:\n' +
|
|
289
|
+
' ./scripts-run src/scripts/check_publish_surface --write\n' +
|
|
290
|
+
' and commit the diff alongside the package.json / .npmignore edit that caused it.\n',
|
|
291
|
+
);
|
|
292
|
+
return 1;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (process.argv[1] !== undefined && process.argv[1].includes('check_publish_surface')) {
|
|
296
|
+
process.exit(main(process.argv.slice(2)));
|
|
297
|
+
}
|
|
@@ -97,6 +97,13 @@ const FILE_SKIP_MARKER = '<!-- check-refs: skip -->';
|
|
|
97
97
|
// fully-checked documents.
|
|
98
98
|
const LINE_IGNORE_MARKER = '<!-- ref-ignore -->';
|
|
99
99
|
|
|
100
|
+
/**
|
|
101
|
+
* The one generated artefact this repository intentionally does not track, so
|
|
102
|
+
* no clone carries it and an existence check cannot answer for it. Referencing
|
|
103
|
+
* it in prose is legitimate; the path simply is not a file in CI.
|
|
104
|
+
*/
|
|
105
|
+
const UNTRACKED_GENERATED_DASHBOARD = 'agents/roadmaps-progress.md';
|
|
106
|
+
|
|
100
107
|
// YAML memory files (engineering-memory layer) live under `agents/memory/`.
|
|
101
108
|
// Each entry may reference skills, ADR paths, or local files via
|
|
102
109
|
// `source:` / `enforcement:` / `skill:`. We validate those paths so a
|
|
@@ -715,6 +722,16 @@ function check_file(
|
|
|
715
722
|
// check above already swallows refs into `agents/runtime/`,
|
|
716
723
|
// so no extra carve-out is needed.
|
|
717
724
|
}
|
|
725
|
+
if (!resolved && rawRef === UNTRACKED_GENERATED_DASHBOARD) {
|
|
726
|
+
// A generated LOCAL artefact this repository deliberately does
|
|
727
|
+
// not commit (2026-08-21: it was the #1 merge-conflict path).
|
|
728
|
+
// It exists on a developer's disk and in no clone, so an
|
|
729
|
+
// existence check is the wrong question for it — same shape as
|
|
730
|
+
// the gitignored `agents/runtime/` carve-out above. Exactly one
|
|
731
|
+
// path, never a prefix: a sibling like `<path>.bak` is still a
|
|
732
|
+
// real broken reference.
|
|
733
|
+
resolved = true;
|
|
734
|
+
}
|
|
718
735
|
if (
|
|
719
736
|
!resolved &&
|
|
720
737
|
_is_frozen_record_source(_relPosix(root, filepath)) &&
|
|
@@ -1168,6 +1185,7 @@ export {
|
|
|
1168
1185
|
SKIP_NAMES,
|
|
1169
1186
|
EXAMPLE_PATH_PATTERNS,
|
|
1170
1187
|
ALLOWLIST_PATTERNS,
|
|
1188
|
+
UNTRACKED_GENERATED_DASHBOARD,
|
|
1171
1189
|
type AllowlistPattern,
|
|
1172
1190
|
_is_allowlisted,
|
|
1173
1191
|
collect_artifacts,
|