@akira-tl/forgerelay 0.9.2 → 0.9.4
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/CHANGELOG.md +17 -0
- package/README.md +9 -3
- package/capabilities/workspace/workspace-checkpoints/GUIDE.md +35 -4
- package/dist/cli/init.js +39 -23
- package/dist/cli/maintenance-prune.js +479 -0
- package/dist/cli/maintenance-retention.js +93 -0
- package/dist/cli/maintenance.js +598 -0
- package/dist/cli/setup-support.js +21 -0
- package/dist/cli.js +29 -6
- package/dist/mcp/oauth/public-url.js +3 -0
- package/dist/mcp/oauth/router.js +14 -9
- package/dist/mcp/server/core/capabilities/workspace-checkpoint.js +9 -0
- package/dist/mcp/server/core/capability-registry.js +1 -1
- package/dist/mcp/server/transport/http-server.js +13 -8
- package/dist/runtime/config/config.js +54 -6
- package/dist/runtime/state/runtime-lease.js +109 -0
- package/dist/server.js +6 -0
- package/dist/workspaces/state/workspace-checkpoints.js +129 -13
- package/docs/configuration.md +100 -13
- package/package.json +2 -2
- package/scripts/ci/architecture.mjs +9 -2
- package/scripts/release/release-gate.test.mjs +6 -0
package/docs/configuration.md
CHANGED
|
@@ -26,6 +26,7 @@ npx @akira-tl/forgerelay serve
|
|
|
26
26
|
npx @akira-tl/forgerelay doctor
|
|
27
27
|
npx @akira-tl/forgerelay config get
|
|
28
28
|
npx @akira-tl/forgerelay config set publicBaseUrl https://forge.example.com/forgerelay/main,https://forge-alt.example.com/relay
|
|
29
|
+
npx @akira-tl/forgerelay maintenance inspect
|
|
29
30
|
```
|
|
30
31
|
|
|
31
32
|
## Environment variables
|
|
@@ -46,6 +47,73 @@ The public environment-variable prefix is `FORGERELAY_*`.
|
|
|
46
47
|
| `FORGERELAY_WORKTREE_ROOT` | Managed worktree directory. New default: `~/.forgerelay/worktrees`. |
|
|
47
48
|
| `FORGERELAY_WORKFLOW_INSTRUCTIONS` | Replace the built-in workflow policy while retaining the capability contract. |
|
|
48
49
|
| `FORGERELAY_APPEND_INSTRUCTIONS` | Append project/operator workflow policy. |
|
|
50
|
+
| `FORGERELAY_RETENTION_HISTORY_DAYS` | Optional owner-authorized age window for related Activity/Audit, Host Turn, and durable Bash history. Unset means unlimited retention. |
|
|
51
|
+
| `FORGERELAY_RETENTION_ORPHANED_ADMIN` | Optional boolean authorization for provably orphaned/rebuildable administrative state. Unset/false means do not reclaim it. |
|
|
52
|
+
|
|
53
|
+
### Retention inspection and prune
|
|
54
|
+
|
|
55
|
+
Durable ForgeRelay history is retained without an age limit by default. The owner can
|
|
56
|
+
inspect what is retained, protected, and potentially reclaimable without starting the
|
|
57
|
+
server:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npx @akira-tl/forgerelay maintenance inspect
|
|
61
|
+
npx @akira-tl/forgerelay maintenance inspect --json
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Inspection is read-only: it snapshots an existing SQLite database and WAL into a
|
|
65
|
+
temporary directory before opening that snapshot read-only, reads bounded
|
|
66
|
+
Workspace-private Task/checkpoint metadata, and uses read-only Git ref/worktree
|
|
67
|
+
queries. It does not create or migrate a missing/older source database, touch Workspace
|
|
68
|
+
last-used timestamps, move refs, or alter worktrees.
|
|
69
|
+
|
|
70
|
+
After reviewing that report, the owner can explicitly apply the configured policy:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
npx @akira-tl/forgerelay maintenance prune
|
|
74
|
+
npx @akira-tl/forgerelay maintenance prune --json
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
`maintenance prune` is manual and policy-gated. With neither `historyDays` nor
|
|
78
|
+
`orphanedAdministrativeState` authorized it is a no-op. It acquires the ForgeRelay
|
|
79
|
+
runtime lease before any destructive work, so it refuses to run concurrently with a
|
|
80
|
+
server using the same state directory. Historical cleanup is performed in whole Host
|
|
81
|
+
Turn cohorts: a turn is retained if any Activity is recent, nonterminal, owns a running
|
|
82
|
+
Bash stream, or is tied to an active Subagent Run. When removed Activity payloads share
|
|
83
|
+
a segmented log with retained payloads, retained bytes are compacted to a new segment
|
|
84
|
+
before the old unreferenced segment is deleted.
|
|
85
|
+
|
|
86
|
+
Administrative cleanup is deliberately conservative. It removes only review refs that
|
|
87
|
+
can be proven orphaned and rebuildable, plus empty orphan Workspace-state directories.
|
|
88
|
+
Canonical and alias Workspace identities, any non-empty private Workspace state,
|
|
89
|
+
Workspace Tasks, named checkpoints, active/runtime state, managed worktrees, and
|
|
90
|
+
managed branches remain protected. Repeating the same prune is idempotent and should
|
|
91
|
+
report no newly eligible state after the first successful pass.
|
|
92
|
+
|
|
93
|
+
The persisted policy shape is:
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"retention": {
|
|
98
|
+
"historyDays": 30,
|
|
99
|
+
"orphanedAdministrativeState": false
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`historyDays` is one shared cutoff for related Activity/Audit, Host Turn, and durable
|
|
105
|
+
Bash history so later owner-authorized maintenance can preserve cross-store
|
|
106
|
+
consistency. Omit it for unlimited durable-history retention. The environment override
|
|
107
|
+
is `FORGERELAY_RETENTION_HISTORY_DAYS`. `orphanedAdministrativeState` is a separate
|
|
108
|
+
explicit authorization, with `FORGERELAY_RETENTION_ORPHANED_ADMIN` as its environment
|
|
109
|
+
override.
|
|
110
|
+
|
|
111
|
+
Named Workspace checkpoints and Workspace Tasks are protected by this policy:
|
|
112
|
+
checkpoint removal remains an explicit checkpoint operation, and retention maintenance
|
|
113
|
+
never treats Tasks as disposable history. Existing automatic runtime GC is separate:
|
|
114
|
+
it bounds rebuildable/in-memory runtime resources and expires stale context-delivery
|
|
115
|
+
bookkeeping; it does **not** age-prune durable Activity/Audit history, durable Bash
|
|
116
|
+
output, named checkpoints, persistent Workspace identity, or Task Lists.
|
|
49
117
|
|
|
50
118
|
### Routed and multi-origin public deployments
|
|
51
119
|
|
|
@@ -61,12 +129,18 @@ or multiple URLs as an array:
|
|
|
61
129
|
}
|
|
62
130
|
```
|
|
63
131
|
|
|
64
|
-
Each entry keeps its own route prefix.
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
132
|
+
Each entry keeps its own route prefix. Every configured pathname is an accepted
|
|
133
|
+
inbound operational route boundary; the first URL remains canonical for generated
|
|
134
|
+
OAuth/MCP metadata and links. For example, if the only configured URL is
|
|
135
|
+
`https://forge.example.com/forgerelay/main`, MCP, OAuth operations, health, and MCP App
|
|
136
|
+
assets are served below `/forgerelay/main/*`; naked `/mcp`, `/authorize`, `/token`,
|
|
137
|
+
`/healthz`, and `/mcp-app-assets/*` are not parallel deployment routes. Standards-based
|
|
138
|
+
OAuth/MCP discovery metadata remains under its required `/.well-known/...` paths.
|
|
139
|
+
Every configured hostname is included in the derived Host-header allowlist. MCP App
|
|
140
|
+
`_meta.ui.domain` uses the canonical URL's origin, while CSP resource/connect entries
|
|
141
|
+
include every full public base URL. The full ordered `publicBaseUrl` list also
|
|
142
|
+
participates in the MCP App resource cache identity, so changing any domain or route
|
|
143
|
+
produces a new `ui://` resource URI.
|
|
70
144
|
|
|
71
145
|
A single persisted string remains fully supported, so existing configs require no
|
|
72
146
|
migration. For environment configuration, use a comma-separated list in
|
|
@@ -632,7 +706,8 @@ from the normal Skill paths; ForgeRelay does not reserve, delete, or rewrite it.
|
|
|
632
706
|
| `FORGERELAY_LOG_ASSETS` | `0` |
|
|
633
707
|
| `FORGERELAY_LOG_TOOL_CALLS` | `1` |
|
|
634
708
|
| `FORGERELAY_LOG_SHELL_COMMANDS` | `1` in `pretty`, `0` in `json` |
|
|
635
|
-
| `FORGERELAY_TRUST_PROXY` |
|
|
709
|
+
| `FORGERELAY_TRUST_PROXY` | legacy compatibility override; `1` is accepted only with a loopback bind |
|
|
710
|
+
| `FORGERELAY_TRUSTED_PROXIES` | explicit comma-separated trusted proxy IP addresses/CIDRs; unset by default |
|
|
636
711
|
|
|
637
712
|
`pretty` is the human-facing local console format. It uses terminal-aware color,
|
|
638
713
|
short timestamps, workspace-first context, and compact operation results while
|
|
@@ -650,12 +725,24 @@ overridden, JSON mode preserves request logging and omits shell command previews
|
|
|
650
725
|
these format-specific defaults when set.
|
|
651
726
|
|
|
652
727
|
When ForgeRelay binds to loopback (`127.0.0.1`, `::1`, or `localhost`) but is
|
|
653
|
-
configured with a non-loopback public URL, it
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
`
|
|
657
|
-
|
|
658
|
-
|
|
728
|
+
configured with a non-loopback public URL, it trusts only the loopback proxy source.
|
|
729
|
+
This matches the normal local tunnel/reverse-proxy topology while preventing a public
|
|
730
|
+
or LAN client from becoming trusted merely because it supplied forwarded headers.
|
|
731
|
+
`forgerelay init` uses this model for **HTTPS reverse proxy / tunnel** mode and binds
|
|
732
|
+
that mode to `127.0.0.1`; **Direct LAN** mode binds to `0.0.0.0` and does not trust a
|
|
733
|
+
proxy by default.
|
|
734
|
+
|
|
735
|
+
Set `FORGERELAY_TRUST_PROXY=0` to disable inferred loopback trust. The legacy
|
|
736
|
+
`FORGERELAY_TRUST_PROXY=1` form is accepted only when ForgeRelay itself is bound to
|
|
737
|
+
loopback. For an advanced topology that intentionally combines direct LAN reachability
|
|
738
|
+
with a reverse proxy, list only the actual proxy source addresses or CIDRs, for example:
|
|
739
|
+
|
|
740
|
+
```bash
|
|
741
|
+
FORGERELAY_TRUSTED_PROXIES="127.0.0.1,10.20.30.0/24" forgerelay serve
|
|
742
|
+
```
|
|
743
|
+
|
|
744
|
+
The same list may be persisted as `trustedProxies` in `config.json`. Wildcard/global
|
|
745
|
+
trust is rejected; do not replace this with Express `trust proxy=true` on a LAN bind.
|
|
659
746
|
|
|
660
747
|
## Environment-only example
|
|
661
748
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akira-tl/forgerelay",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.4",
|
|
4
4
|
"description": "Local development control plane for MCP coding agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"homepage": "https://github.com/Akira-TL/forgerelay#readme",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"release:push-ready": "node scripts/release/push-ready.mjs",
|
|
53
53
|
"postinstall": "node scripts/fix-node-pty-permissions.mjs",
|
|
54
54
|
"start": "node dist/cli.js serve",
|
|
55
|
-
"test": "node --test scripts/debug/runtime.test.mjs scripts/release-proof.test.mjs scripts/release/release-gate.test.mjs scripts/release/push-ready.test.mjs scripts/release/release-version.test.mjs && tsx src/mcp/oauth/router.test.ts && tsx src/workspaces/relay/auth/remote-auth-cli.test.ts && tsx src/workspaces/relay/auth/remote-ssh-auth-cli.test.ts && tsx src/workspaces/relay/tests/lifecycle.test.ts && tsx src/workspaces/relay/tests/routing.test.ts && tsx src/workspaces/relay/tests/ssh.test.ts && tsx src/workspaces/relay/tests/process.test.ts && tsx src/workspaces/relay/tests/recovery.test.ts && tsx src/workspaces/relay/tests/checkpoint.test.ts && tsx src/runtime/config/config.test.ts && tsx src/lsp/language-server-config.test.ts && tsx src/lsp/runtime/managed-language-servers.test.ts && tsx src/lsp/normalization/hover.test.ts && tsx src/lsp/references.server.test.ts && tsx src/lsp/operations/document-symbols.server.test.ts && tsx src/lsp/operations/workspace-symbols.server.test.ts && tsx src/lsp/operations/diagnostics-push.server.test.ts && tsx src/lsp/operations/diagnostics-pull.server.test.ts && tsx src/lsp/operations/request-hardening.server.test.ts && tsx src/lsp/operations/recovery.server.test.ts && tsx src/lsp/operations/lifecycle.server.test.ts && tsx src/lsp/runtime/semantic-requests.test.ts && tsx src/runtime/logging/logger.test.ts && tsx src/runtime/logging/proxy-trust.test.ts && tsx src/runtime/state/lock/file-lock.test.ts && tsx src/mcp/panel/mcp-app-template.test.ts && tsx src/mcp/hooks/hooks.test.ts && tsx src/mcp/server/core/capability-registry.test.ts && tsx src/mcp/server-instructions.test.ts && tsx src/mcp/request-meta.test.ts && tsx src/mcp/artifacts/incoming-artifacts.test.ts && tsx src/mcp/artifacts/artifact-download.test.ts && tsx src/ui/core/card-types.test.ts && tsx src/ui/activity/model.test.ts && tsx src/ui/activity/detail-card.test.ts && tsx src/ui/review/patch-display.test.ts && tsx src/ui/core/tool-display.test.ts && tsx src/mcp/filesystem/apply-patch.test.ts && tsx src/mcp/process/process-platform.test.ts && tsx src/mcp/process/process-sessions.test.ts && tsx src/mcp/server/transport/mcp-sessions.test.ts && tsx src/mcp/server/transport/server-shutdown.test.ts && tsx src/mcp/server/operations/mutation-diagnostics.test.ts && tsx src/subagents/providers/adapters/codex.test.ts && tsx src/subagents/providers/adapters/pi.test.ts && tsx src/subagents/providers/registry.test.ts && tsx src/subagents/providers/availability.test.ts && tsx src/subagents/profiles.test.ts && tsx src/subagents/cli-target.test.ts && tsx src/subagents/sessions/store.test.ts && tsx src/subagents/sessions/manager.test.ts && tsx src/subagents/sessions/mcp/capability.server.test.ts && tsx src/subagents/sessions/mcp/continuation.server.test.ts && tsx src/subagents/sessions/mcp/lifecycle.server.test.ts && tsx src/subagents/sessions/mcp/reconciliation.server.test.ts && tsx src/subagents/sessions/mcp/routing.server.test.ts && tsx src/mcp/filesystem/roots.test.ts && tsx src/mcp/filesystem/file-mutations.test.ts && tsx src/mcp/operations/edit-preflight.test.ts && tsx src/workspaces/resources/skills.test.ts && tsx src/runtime/state/db/migrations.test.ts && tsx src/workspaces/state/workspace-store.test.ts && tsx src/workspaces/tasks/workspace-tasks.test.ts && tsx src/workspaces/tasks/workspace-task-reminders.test.ts && tsx src/activity/history/audit-store.test.ts && tsx src/activity/history/bash-output-store.test.ts && tsx src/activity/runtime/lifecycle.test.ts && tsx src/activity/history/query-service.test.ts && tsx src/mcp/operations/core-operation-executor.test.ts && tsx src/mcp/operations/bulk-mutation.test.ts && tsx src/mcp/operations/batch/scheduler.test.ts && tsx src/mcp/operations/batch/executor-policy.test.ts && tsx src/workspaces.test.ts && tsx src/workspaces/conversation-checkout.test.ts && tsx src/workspaces/conversation-worktree.test.ts && tsx src/workspaces/git/worktree-recovery.test.ts && tsx src/mcp/server/workspace/workspace-inventory.test.ts && tsx src/mcp/server/workspace/workspace-recovery.test.ts && tsx src/mcp/server/workspace/workspace-checkpoint.test.ts && tsx src/workspaces/review/review-checkpoints.test.ts && tsx src/lsp/code-intelligence.server.test.ts && npm run build:app && tsx src/mcp/process/server.test.ts && tsx src/mcp/panel/server.test.ts && tsx src/mcp/server/server.test.ts && tsx src/mcp/oauth/oauth-store.test.ts && tsx src/cli/cli.test.ts",
|
|
55
|
+
"test": "node --test scripts/debug/runtime.test.mjs scripts/release-proof.test.mjs scripts/release/release-gate.test.mjs scripts/release/push-ready.test.mjs scripts/release/release-version.test.mjs && tsx src/mcp/oauth/router.test.ts && tsx src/workspaces/relay/auth/remote-auth-cli.test.ts && tsx src/workspaces/relay/auth/remote-ssh-auth-cli.test.ts && tsx src/workspaces/relay/tests/lifecycle.test.ts && tsx src/workspaces/relay/tests/routing.test.ts && tsx src/workspaces/relay/tests/ssh.test.ts && tsx src/workspaces/relay/tests/process.test.ts && tsx src/workspaces/relay/tests/recovery.test.ts && tsx src/workspaces/relay/tests/checkpoint.test.ts && tsx src/runtime/config/config.test.ts && tsx src/lsp/language-server-config.test.ts && tsx src/lsp/runtime/managed-language-servers.test.ts && tsx src/lsp/normalization/hover.test.ts && tsx src/lsp/references.server.test.ts && tsx src/lsp/operations/document-symbols.server.test.ts && tsx src/lsp/operations/workspace-symbols.server.test.ts && tsx src/lsp/operations/diagnostics-push.server.test.ts && tsx src/lsp/operations/diagnostics-pull.server.test.ts && tsx src/lsp/operations/request-hardening.server.test.ts && tsx src/lsp/operations/recovery.server.test.ts && tsx src/lsp/operations/lifecycle.server.test.ts && tsx src/lsp/runtime/semantic-requests.test.ts && tsx src/runtime/logging/logger.test.ts && tsx src/runtime/logging/proxy-trust.test.ts && tsx src/runtime/state/lock/file-lock.test.ts && tsx src/runtime/state/runtime-lease.test.ts && tsx src/mcp/panel/mcp-app-template.test.ts && tsx src/mcp/hooks/hooks.test.ts && tsx src/mcp/server/core/capability-registry.test.ts && tsx src/mcp/server-instructions.test.ts && tsx src/mcp/request-meta.test.ts && tsx src/mcp/artifacts/incoming-artifacts.test.ts && tsx src/mcp/artifacts/artifact-download.test.ts && tsx src/ui/core/card-types.test.ts && tsx src/ui/activity/model.test.ts && tsx src/ui/activity/detail-card.test.ts && tsx src/ui/review/patch-display.test.ts && tsx src/ui/core/tool-display.test.ts && tsx src/mcp/filesystem/apply-patch.test.ts && tsx src/mcp/process/process-platform.test.ts && tsx src/mcp/process/process-sessions.test.ts && tsx src/mcp/server/transport/mcp-sessions.test.ts && tsx src/mcp/server/transport/server-shutdown.test.ts && tsx src/mcp/server/operations/mutation-diagnostics.test.ts && tsx src/subagents/providers/adapters/codex.test.ts && tsx src/subagents/providers/adapters/pi.test.ts && tsx src/subagents/providers/registry.test.ts && tsx src/subagents/providers/availability.test.ts && tsx src/subagents/profiles.test.ts && tsx src/subagents/cli-target.test.ts && tsx src/subagents/sessions/store.test.ts && tsx src/subagents/sessions/manager.test.ts && tsx src/subagents/sessions/mcp/capability.server.test.ts && tsx src/subagents/sessions/mcp/continuation.server.test.ts && tsx src/subagents/sessions/mcp/lifecycle.server.test.ts && tsx src/subagents/sessions/mcp/reconciliation.server.test.ts && tsx src/subagents/sessions/mcp/routing.server.test.ts && tsx src/mcp/filesystem/roots.test.ts && tsx src/mcp/filesystem/file-mutations.test.ts && tsx src/mcp/operations/edit-preflight.test.ts && tsx src/workspaces/resources/skills.test.ts && tsx src/runtime/state/db/migrations.test.ts && tsx src/workspaces/state/workspace-store.test.ts && tsx src/workspaces/tasks/workspace-tasks.test.ts && tsx src/workspaces/tasks/workspace-task-reminders.test.ts && tsx src/activity/history/audit-store.test.ts && tsx src/activity/history/bash-output-store.test.ts && tsx src/activity/runtime/lifecycle.test.ts && tsx src/activity/history/query-service.test.ts && tsx src/mcp/operations/core-operation-executor.test.ts && tsx src/mcp/operations/bulk-mutation.test.ts && tsx src/mcp/operations/batch/scheduler.test.ts && tsx src/mcp/operations/batch/executor-policy.test.ts && tsx src/workspaces.test.ts && tsx src/workspaces/conversation-checkout.test.ts && tsx src/workspaces/conversation-worktree.test.ts && tsx src/workspaces/git/worktree-recovery.test.ts && tsx src/mcp/server/workspace/workspace-inventory.test.ts && tsx src/mcp/server/workspace/workspace-recovery.test.ts && tsx src/mcp/server/workspace/workspace-checkpoint.test.ts && tsx src/workspaces/review/review-checkpoints.test.ts && tsx src/lsp/code-intelligence.server.test.ts && npm run build:app && tsx src/mcp/process/server.test.ts && tsx src/mcp/panel/server.test.ts && tsx src/mcp/server/server.test.ts && tsx src/mcp/oauth/oauth-store.test.ts && tsx src/cli/maintenance.test.ts && tsx src/cli/maintenance-prune.test.ts && tsx src/cli/cli.test.ts",
|
|
56
56
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
57
57
|
"release:check": "node scripts/release-version.mjs check",
|
|
58
58
|
"release:tag-check": "node scripts/release-version.mjs tag",
|
|
@@ -9,6 +9,13 @@ const MAX_DIRECT_FILES = 8;
|
|
|
9
9
|
const MAX_DIRECT_DIRS = 8;
|
|
10
10
|
const LINE_LIMIT_EXTENSIONS = new Set([".ts", ".tsx", ".js", ".mjs", ".cjs", ".css"]);
|
|
11
11
|
|
|
12
|
+
// Append-only versioned archives grow by design and must keep their canonical
|
|
13
|
+
// flat paths for release tooling and stable links. They remain subject to all
|
|
14
|
+
// other architecture checks, including code line limits where applicable.
|
|
15
|
+
const DIRECT_FILE_LIMIT_EXEMPT_DIRS = new Set([
|
|
16
|
+
"docs/releases",
|
|
17
|
+
]);
|
|
18
|
+
|
|
12
19
|
// Repository-root protocol files are intentionally discoverable by Git, npm,
|
|
13
20
|
// Node, Vite, contributors, and agents. Keep that conventional surface explicit
|
|
14
21
|
// instead of hiding required/default-discovery files merely to satisfy a count.
|
|
@@ -67,7 +74,7 @@ const directories = new Set([...directFiles.keys(), ...directDirs.keys()]);
|
|
|
67
74
|
for (const directory of [...directories].sort()) {
|
|
68
75
|
const files = directFiles.get(directory) ?? 0;
|
|
69
76
|
const dirs = directDirs.get(directory)?.size ?? 0;
|
|
70
|
-
if (directory !== "." && files > MAX_DIRECT_FILES) {
|
|
77
|
+
if (directory !== "." && !DIRECT_FILE_LIMIT_EXEMPT_DIRS.has(directory) && files > MAX_DIRECT_FILES) {
|
|
71
78
|
violations.push(`${directory}: ${files} direct files > ${MAX_DIRECT_FILES}`);
|
|
72
79
|
}
|
|
73
80
|
if (dirs > MAX_DIRECT_DIRS) {
|
|
@@ -83,7 +90,7 @@ if (violations.length > 0) {
|
|
|
83
90
|
|
|
84
91
|
console.log(
|
|
85
92
|
`Architecture check passed: ${tracked.length} tracked files; ` +
|
|
86
|
-
`code <= ${MAX_LINES} lines; non-root directories <= ${MAX_DIRECT_FILES} files / ${MAX_DIRECT_DIRS} directories.`,
|
|
93
|
+
`code <= ${MAX_LINES} lines; bounded non-root directories <= ${MAX_DIRECT_FILES} files / ${MAX_DIRECT_DIRS} directories.`,
|
|
87
94
|
);
|
|
88
95
|
|
|
89
96
|
function gitTrackedFiles() {
|
|
@@ -60,6 +60,12 @@ test("cross-platform cloud CI delegates to one shell-free verification entrypoin
|
|
|
60
60
|
}
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
+
test("architecture gate treats the append-only release-note archive as an explicit flat-path exception", async () => {
|
|
64
|
+
const architecture = await readFile(resolve(repoRoot, "scripts/ci/architecture.mjs"), "utf8");
|
|
65
|
+
assert.match(architecture, /DIRECT_FILE_LIMIT_EXEMPT_DIRS/);
|
|
66
|
+
assert.match(architecture, /"docs\/releases"/);
|
|
67
|
+
});
|
|
68
|
+
|
|
63
69
|
test("release runtime and local parity share the checked-in Node contract", async () => {
|
|
64
70
|
const nodeVersion = (await readFile(resolve(repoRoot, ".nvmrc"), "utf8")).trim();
|
|
65
71
|
assert.equal(nodeVersion, "22.19.0");
|