@moretea-labs/forge 1.7.0 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -0
- package/CHANGELOG.md +13 -0
- package/CLAUDE.md +2 -0
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/agents/openai.yaml +1 -1
- package/assets/partials-agents/02-operating-mode.partial.md +2 -0
- package/assets/plugin-registry.v1.json +3 -3
- package/assets/skill-version.json +2 -2
- package/assets/templates/helpers/check-architecture-sync.sh +17 -0
- package/assets/templates/helpers/inspect-project-state.ts +8 -6
- package/assets/templates/helpers/migrate-workflow-docs.ts +9 -7
- package/assets/templates/helpers/workflow-contract.ts +5 -1
- package/docs/architecture/CURRENT.md +12 -4
- package/docs/forge-plugin-management.md +1 -1
- package/docs/operations/controller-performance-and-502.md +19 -0
- package/docs/operations/homebrew.md +2 -2
- package/package.json +5 -2
- package/scripts/architecture-event.ts +2 -820
- package/scripts/architecture-queue.sh +2 -645
- package/scripts/archive-architecture-request.sh +2 -242
- package/scripts/archive-workflow.sh +1 -291
- package/scripts/benchmark-work-efficiency.ts +0 -0
- package/scripts/capability-config.ts +2 -392
- package/scripts/capability-resolver.ts +2 -503
- package/scripts/capture-plan.sh +1 -332
- package/scripts/check-agent-tooling.sh +2 -1531
- package/scripts/check-architecture-sync.sh +2 -302
- package/scripts/check-brain-manifest.sh +2 -253
- package/scripts/check-context-files.sh +2 -47
- package/scripts/check-deploy-sql-order.sh +2 -115
- package/scripts/check-runtime-architecture.mjs +243 -16
- package/scripts/check-skill-version.ts +3 -191
- package/scripts/check-source-duplication.mjs +61 -0
- package/scripts/check-stable-baseline.ts +0 -0
- package/scripts/check-task-sync.sh +2 -63
- package/scripts/check-task-workflow.sh +2 -1000
- package/scripts/codex-handoff-resume.sh +2 -267
- package/scripts/context-contract-sync.sh +2 -514
- package/scripts/contract-run.ts +2 -463
- package/scripts/contract-worktree.sh +1 -702
- package/scripts/ensure-task-workflow.sh +1 -1192
- package/scripts/inspect-project-state.ts +4 -279
- package/scripts/lib/project-init-lib.sh +7 -2
- package/scripts/lib/run-forge-helper.sh +19 -0
- package/scripts/lib/run-forge-helper.ts +26 -0
- package/scripts/migrate-project-template.sh +1 -33
- package/scripts/migrate-workflow-docs.ts +4 -412
- package/scripts/new-plan.sh +1 -180
- package/scripts/new-spec.sh +1 -47
- package/scripts/new-sprint.sh +1 -46
- package/scripts/plan-to-todo.sh +1 -771
- package/scripts/prepare-codex-handoff.sh +2 -201
- package/scripts/prepare-handoff.sh +1 -78
- package/scripts/refresh-current-status.sh +2 -467
- package/scripts/run-governed-gate.ts +3 -0
- package/scripts/select-agent-context-blocks.sh +2 -64
- package/scripts/ship-worktrees.sh +1 -531
- package/scripts/sprint-backlog.sh +1 -834
- package/scripts/stage-runtime-release.ts +2 -1
- package/scripts/summarize-failures.sh +2 -123
- package/scripts/switch-plan.sh +1 -150
- package/scripts/sync-brain-docs.sh +2 -379
- package/scripts/sync-generated-authority.mjs +175 -0
- package/scripts/verify-contract.sh +2 -593
- package/scripts/verify-sprint.sh +1 -500
- package/scripts/workflow-contract.ts +4 -201
- package/scripts/workstream-sync.sh +2 -261
- package/src/cli/agent-jobs/executor-health.ts +1 -1
- package/src/cli/commands/plugin.ts +12 -3
- package/src/cli/controller/check-runner.ts +7 -2
- package/src/cli/controller/context-pack.ts +46 -21
- package/src/cli/github/contracts.ts +16 -0
- package/src/cli/github/github.ts +2 -16
- package/src/cli/github/session.ts +2 -1
- package/src/cli/local-bridge/facade-api.ts +37 -47
- package/src/cli/local-bridge/server.ts +2 -2
- package/src/cli/mcp/legacy-tool-service.ts +10 -33
- package/src/cli/mcp/post-finalize-work-attribution.ts +150 -0
- package/src/cli/mcp/repository-tools.ts +85 -89
- package/src/cli/mcp/server.ts +57 -13
- package/src/cli/mcp/tool-contract.ts +14 -0
- package/src/cli/mcp/toolset.ts +2 -2
- package/src/cli/mcp/transports/http.ts +23 -0
- package/src/cli/repositories/command-classifier.ts +2 -2
- package/src/cli/repositories/command-executor.ts +24 -6
- package/src/cli/repositories/repository-snapshot.ts +5 -2
- package/src/cli/repositories/structured-git.ts +57 -3
- package/src/cli/repository/inspector.ts +11 -3
- package/src/runtime/context/generic-lsp-provider.ts +1 -1
- package/src/runtime/context/semantic-navigation-contract.ts +61 -0
- package/src/runtime/context/semantic-navigation.ts +9 -61
- package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +18 -25
- package/src/runtime/control-plane/execution/repository-work-attribution.ts +106 -0
- package/src/runtime/control-plane/execution/retained-work-resume.ts +203 -0
- package/src/runtime/control-plane/execution/work-completion-authority.ts +58 -0
- package/src/runtime/control-plane/execution/work-evidence-policy.ts +200 -0
- package/src/runtime/control-plane/execution/work-execution-support.ts +386 -0
- package/src/runtime/control-plane/execution/work-finalization-service.ts +1913 -0
- package/src/runtime/control-plane/execution/work-handle-authority.ts +96 -0
- package/src/runtime/control-plane/execution/work-handle-store.ts +1 -1
- package/src/runtime/control-plane/execution/work-operation-service.ts +468 -0
- package/src/runtime/control-plane/execution/work-path-scope.ts +37 -0
- package/src/runtime/control-plane/execution/work-preparation-service.ts +479 -0
- package/src/runtime/control-plane/execution/work-process-evidence.ts +129 -0
- package/src/runtime/control-plane/execution/work-task-receipt.ts +4 -43
- package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +13 -3
- package/src/runtime/control-plane/execution/work-verification-context.ts +56 -0
- package/src/runtime/control-plane/execution/work-verification-service.ts +388 -0
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +5 -1
- package/src/runtime/control-plane/facade/controller-round-relay.ts +164 -37
- package/src/runtime/control-plane/facade/controller-session-store.ts +183 -24
- package/src/runtime/control-plane/facade/goal-workloop-access.ts +15 -1
- package/src/runtime/control-plane/facade/goal-workloop.ts +329 -148
- package/src/runtime/control-plane/facade/index.ts +2 -0
- package/src/runtime/control-plane/facade/plan-contract-store.ts +90 -22
- package/src/runtime/control-plane/facade/repository-work-admission.ts +147 -0
- package/src/runtime/control-plane/facade/requirement-authority.ts +83 -0
- package/src/runtime/control-plane/facade/types.ts +68 -5
- package/src/runtime/control-plane/facade/work-contract-store.ts +3 -209
- package/src/runtime/control-plane/facade/work-state-machine.ts +250 -0
- package/src/runtime/control-plane/global-scheduler/maintenance.ts +14 -7
- package/src/runtime/control-plane/global-scheduler/scheduler.ts +5 -1
- package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +13 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +15 -6
- package/src/runtime/control-plane/launcher/launch-reservation-store.ts +54 -5
- package/src/runtime/control-plane/launcher/thin-launcher.ts +56 -7
- package/src/runtime/control-plane/persistence/requirement-store.ts +0 -12
- package/src/runtime/control-plane/repo-actor/registry.ts +4 -2
- package/src/runtime/control-plane/routing/route-policy.ts +18 -6
- package/src/runtime/control-plane/routing/workspace-admission.ts +70 -0
- package/src/runtime/evidence/process-check-execution.ts +18 -0
- package/src/runtime/execution/jobs/child-reference.ts +6 -2
- package/src/runtime/execution/process-runtime/check-result.ts +53 -18
- package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +7 -1
- package/src/runtime/execution/process-runtime/command-facade.ts +39 -17
- package/src/runtime/execution/process-runtime/lightweight-managed.ts +20 -3
- package/src/runtime/execution/process-runtime/persisted-check.ts +4 -1
- package/src/runtime/execution/process-runtime/runtime.ts +10 -0
- package/src/runtime/execution/process-runtime/types.ts +2 -12
- package/src/runtime/execution/work-activity.ts +21 -0
- package/src/runtime/execution/workers/executor.ts +18 -13
- package/src/runtime/gateway/mcp/execution-tools.ts +35 -2930
- package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +61 -0
- package/src/runtime/gateway/mcp/router.ts +2 -2
- package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +21 -2
- package/src/runtime/gateway/mcp/runtime-tools.ts +805 -1009
- package/src/runtime/plugins/app-store-connect-adapter.ts +93 -28
- package/src/runtime/plugins/browser-adapter.ts +256 -1083
- package/src/runtime/plugins/browser-handoff-host.ts +2 -25
- package/src/runtime/plugins/browser-handoff.ts +18 -3
- package/src/runtime/plugins/browser-macos-bridge.ts +121 -16
- package/src/runtime/plugins/browser-manifest-surface.ts +603 -0
- package/src/runtime/plugins/browser-native-session-service.ts +222 -0
- package/src/runtime/plugins/browser-runtime-contract.ts +2 -0
- package/src/runtime/plugins/browser-runtime.ts +10 -0
- package/src/runtime/plugins/browser-session-store.ts +121 -0
- package/src/runtime/plugins/browser-session-types.ts +98 -0
- package/src/runtime/plugins/desktop-operator-registration.ts +4 -4
- package/src/runtime/plugins/external-adapter.ts +92 -58
- package/src/runtime/plugins/macos-capability-broker.ts +0 -8
- package/src/runtime/plugins/store.ts +66 -9
- package/src/runtime/plugins/types.ts +3 -1
- package/src/runtime/recovery/maintenance-executor.ts +61 -25
- package/src/runtime/root/package-connector-service.ts +46 -4
- package/src/runtime/root/package-runtime-release.ts +1 -1
- package/src/runtime/root/package-runtime-service.ts +33 -2
- package/src/runtime/root/release-manifest.ts +137 -16
- package/src/runtime/root/release-materialize.ts +109 -35
- package/src/runtime/root/types.ts +10 -0
- package/src/runtime/standalone-recovery/core.ts +425 -194
- package/src/runtime/standalone-recovery/entry.ts +1 -1
- package/src/runtime/standalone-recovery/installer.ts +1 -1
- package/src/runtime/workflow/schedules/engine.ts +113 -87
- package/src/runtime/workflow/schedules/settlement.ts +26 -23
- package/src/runtime/workflow/schedules/store.ts +79 -10
|
@@ -1,202 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import {
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
export * from "../assets/templates/helpers/workflow-contract";
|
|
3
|
+
import { runTrackedForgeHelper } from "./lib/run-forge-helper";
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
version: string;
|
|
7
|
-
contractId: string;
|
|
8
|
-
compatibility: {
|
|
9
|
-
agents: string[];
|
|
10
|
-
repoLocalFirst: boolean;
|
|
11
|
-
};
|
|
12
|
-
externalTooling?: {
|
|
13
|
-
waza?: {
|
|
14
|
-
sourceRepo: string;
|
|
15
|
-
managedSkills: string[];
|
|
16
|
-
primaryHost: string;
|
|
17
|
-
codexPrimaryPath: string;
|
|
18
|
-
stagingCachePath: string;
|
|
19
|
-
syncMode: string;
|
|
20
|
-
hostDriftPolicy: string;
|
|
21
|
-
};
|
|
22
|
-
codexAutomationProfile?: {
|
|
23
|
-
requiredSkills: string[];
|
|
24
|
-
optionalSkills: string[];
|
|
25
|
-
mode: string;
|
|
26
|
-
source: string;
|
|
27
|
-
routes: {
|
|
28
|
-
workflowHealth: string;
|
|
29
|
-
reviewGate: string;
|
|
30
|
-
architectureDiagram: string;
|
|
31
|
-
};
|
|
32
|
-
vendoringPolicy: string;
|
|
33
|
-
};
|
|
34
|
-
diagramDesign?: {
|
|
35
|
-
skillName: string;
|
|
36
|
-
primaryHost: string;
|
|
37
|
-
codexPrimaryPath: string;
|
|
38
|
-
syncMode: string;
|
|
39
|
-
vendoringPolicy: string;
|
|
40
|
-
};
|
|
41
|
-
};
|
|
42
|
-
agenticDevelopment?: {
|
|
43
|
-
routing: {
|
|
44
|
-
productDiscovery: string;
|
|
45
|
-
complexEngineeringPlan: string;
|
|
46
|
-
designPlan: string;
|
|
47
|
-
smallOrMediumPlan: string;
|
|
48
|
-
bugOrRegression: string;
|
|
49
|
-
postImplementationReview: string;
|
|
50
|
-
};
|
|
51
|
-
dueDiligence: {
|
|
52
|
-
levels: string[];
|
|
53
|
-
explicitReportRequiredFor: string[];
|
|
54
|
-
};
|
|
55
|
-
};
|
|
56
|
-
documentation?: {
|
|
57
|
-
referenceConfigs?: {
|
|
58
|
-
source: string;
|
|
59
|
-
repoStubDirectory: string;
|
|
60
|
-
packageDirectory: string;
|
|
61
|
-
resolverCommand: string;
|
|
62
|
-
stubMarker: string;
|
|
63
|
-
};
|
|
64
|
-
};
|
|
65
|
-
helpers: {
|
|
66
|
-
runtimeDirectory?: string;
|
|
67
|
-
compatibilityDirectory?: string;
|
|
68
|
-
scripts: string[];
|
|
69
|
-
};
|
|
70
|
-
artifacts: {
|
|
71
|
-
runtimeManifest: string;
|
|
72
|
-
requiredDirectories: string[];
|
|
73
|
-
requiredFiles: string[];
|
|
74
|
-
runtimeFiles?: string[];
|
|
75
|
-
};
|
|
76
|
-
documents: {
|
|
77
|
-
spec: string;
|
|
78
|
-
currentStatus?: string;
|
|
79
|
-
planDirectory: string;
|
|
80
|
-
taskChecklist?: string;
|
|
81
|
-
deferredGoalLedger?: string;
|
|
82
|
-
researchReportsDirectory: string;
|
|
83
|
-
lessonsLog: string;
|
|
84
|
-
};
|
|
85
|
-
adoptionTemplates?: {
|
|
86
|
-
files?: Record<
|
|
87
|
-
string,
|
|
88
|
-
{
|
|
89
|
-
document: string;
|
|
90
|
-
reason: string;
|
|
91
|
-
lines: string[];
|
|
92
|
-
}
|
|
93
|
-
>;
|
|
94
|
-
};
|
|
95
|
-
migrations: {
|
|
96
|
-
legacyVersions: string[];
|
|
97
|
-
legacyPaths: string[];
|
|
98
|
-
upgrade?: {
|
|
99
|
-
strategyVersion: number;
|
|
100
|
-
supportedLegacyVersions: string[];
|
|
101
|
-
actionClasses: string[];
|
|
102
|
-
safety: {
|
|
103
|
-
removeOnlyOwnership: string;
|
|
104
|
-
unknownFiles: string;
|
|
105
|
-
customHooks: string;
|
|
106
|
-
ignoredReferenceMaterial: string;
|
|
107
|
-
localSecrets: string;
|
|
108
|
-
};
|
|
109
|
-
actions: Array<{
|
|
110
|
-
id: string;
|
|
111
|
-
signal: string;
|
|
112
|
-
action: "preserve" | "archive" | "reconfigure" | "remove";
|
|
113
|
-
risk: "low" | "medium" | "high";
|
|
114
|
-
ownership: "known_generated" | "managed_config" | "user_authored" | "user_local";
|
|
115
|
-
paths: string[];
|
|
116
|
-
targetPaths?: string[];
|
|
117
|
-
cleanupMode?: "always" | "generated_helper";
|
|
118
|
-
summary: string;
|
|
119
|
-
}>;
|
|
120
|
-
};
|
|
121
|
-
};
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
|
|
125
|
-
const REPO_ROOT = SCRIPT_DIR.endsWith("/.ai/harness/scripts")
|
|
126
|
-
? join(SCRIPT_DIR, "../../..")
|
|
127
|
-
: SCRIPT_DIR.endsWith("/assets/templates/helpers")
|
|
128
|
-
? join(SCRIPT_DIR, "../../..")
|
|
129
|
-
: join(SCRIPT_DIR, "..");
|
|
130
|
-
const LOCAL_ASSET_PATH = join(REPO_ROOT, "assets", "workflow-contract.v1.json");
|
|
131
|
-
|
|
132
|
-
export function resolveAgenticDevRoot(_repoRoot = REPO_ROOT): string {
|
|
133
|
-
const configuredRoot =
|
|
134
|
-
process.env.AGENTIC_DEV_ROOT ||
|
|
135
|
-
process.env.AGENTIC_DEV_SKILL_ROOT;
|
|
136
|
-
if (configuredRoot && configuredRoot.length > 0) return configuredRoot;
|
|
137
|
-
|
|
138
|
-
if (existsSync(LOCAL_ASSET_PATH)) return REPO_ROOT;
|
|
139
|
-
|
|
140
|
-
const home = process.env.HOME;
|
|
141
|
-
if (home && home.length > 0) {
|
|
142
|
-
const candidates = [
|
|
143
|
-
join(home, "Projects", "forge"),
|
|
144
|
-
join(home, ".codex", "skills", "forge"),
|
|
145
|
-
join(home, ".claude", "skills", "forge"),
|
|
146
|
-
join(home, ".agents", "skills", "forge"),
|
|
147
|
-
];
|
|
148
|
-
|
|
149
|
-
for (const candidate of candidates) {
|
|
150
|
-
if (existsSync(candidate)) return candidate;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return candidates[0];
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
return join(".forge", "skills", "forge");
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
export function resolveAgenticDevSkillRoot(repoRoot = REPO_ROOT): string {
|
|
160
|
-
return resolveAgenticDevRoot(repoRoot);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
export function resolveUpstreamWorkflowContract(repoRoot = REPO_ROOT): string {
|
|
164
|
-
if (existsSync(LOCAL_ASSET_PATH)) return LOCAL_ASSET_PATH;
|
|
165
|
-
return join(resolveAgenticDevRoot(repoRoot), "assets", "workflow-contract.v1.json");
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
export function loadWorkflowContract(
|
|
169
|
-
contractPath = resolveUpstreamWorkflowContract()
|
|
170
|
-
): WorkflowContract {
|
|
171
|
-
return JSON.parse(readFileSync(contractPath, "utf-8")) as WorkflowContract;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
export function resolveInstalledWorkflowContract(repoRoot: string): string {
|
|
175
|
-
return join(repoRoot, ".ai", "harness", "workflow-contract.json");
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
export function resolveWorkflowContractForRepo(repoRoot: string): string {
|
|
179
|
-
const installedPath = resolveInstalledWorkflowContract(repoRoot);
|
|
180
|
-
return existsSync(installedPath) ? installedPath : resolveUpstreamWorkflowContract(repoRoot);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
export function getHelperScripts(contract: WorkflowContract): string[] {
|
|
184
|
-
return [...contract.helpers.scripts];
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
export function getHelperRuntimeDir(contract: WorkflowContract): string {
|
|
188
|
-
return contract.helpers.runtimeDirectory ?? contract.helpers.compatibilityDirectory ?? "scripts";
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
export function getRequiredDirectories(contract: WorkflowContract): string[] {
|
|
192
|
-
return [...contract.artifacts.requiredDirectories];
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
export function getRequiredFiles(contract: WorkflowContract): string[] {
|
|
196
|
-
return [...contract.artifacts.requiredFiles];
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
if (import.meta.main) {
|
|
200
|
-
const contract = loadWorkflowContract(process.argv[2] || resolveUpstreamWorkflowContract());
|
|
201
|
-
console.log(JSON.stringify(contract, null, 2));
|
|
202
|
-
}
|
|
5
|
+
if (import.meta.main) runTrackedForgeHelper("workflow-contract.ts");
|
|
@@ -1,263 +1,4 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
set -euo pipefail
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
usage() {
|
|
7
|
-
cat <<'USAGE_EOF'
|
|
8
|
-
Usage:
|
|
9
|
-
scripts/workstream-sync.sh ensure --block <capability-prefix> [--slug <slug>] [--title <title>] [--plan <plan-file>] [--slice <todo-id>] [--request <architecture-request>]
|
|
10
|
-
USAGE_EOF
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
repo="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
|
|
14
|
-
repo="$(cd "$repo" && pwd)"
|
|
15
|
-
cd "$repo"
|
|
16
|
-
|
|
17
|
-
command_name="${1:-ensure}"
|
|
18
|
-
shift || true
|
|
19
|
-
|
|
20
|
-
functional_block=""
|
|
21
|
-
slug=""
|
|
22
|
-
title=""
|
|
23
|
-
source_plan="(none)"
|
|
24
|
-
current_slice="todo-01"
|
|
25
|
-
request_file="(none)"
|
|
26
|
-
|
|
27
|
-
json_escape() {
|
|
28
|
-
local value="$1"
|
|
29
|
-
value="${value//\\/\\\\}"
|
|
30
|
-
value="${value//\"/\\\"}"
|
|
31
|
-
value="${value//$'\n'/\\n}"
|
|
32
|
-
value="${value//$'\r'/\\r}"
|
|
33
|
-
value="${value//$'\t'/\\t}"
|
|
34
|
-
printf '%s' "$value"
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
json_get() {
|
|
38
|
-
local json_input="$1"
|
|
39
|
-
local key="$2"
|
|
40
|
-
local parsed=""
|
|
41
|
-
|
|
42
|
-
if [[ -z "$json_input" ]]; then
|
|
43
|
-
return 1
|
|
44
|
-
fi
|
|
45
|
-
|
|
46
|
-
if command -v jq >/dev/null 2>&1; then
|
|
47
|
-
parsed="$(printf '%s' "$json_input" | jq -r ".$key // empty" 2>/dev/null || true)"
|
|
48
|
-
fi
|
|
49
|
-
|
|
50
|
-
if [[ -z "$parsed" ]] && command -v node >/dev/null 2>&1; then
|
|
51
|
-
parsed="$(JSON_INPUT="$json_input" JSON_KEY="$key" node -e '
|
|
52
|
-
const raw = process.env.JSON_INPUT || "";
|
|
53
|
-
const key = process.env.JSON_KEY || "";
|
|
54
|
-
try {
|
|
55
|
-
const value = JSON.parse(raw)[key];
|
|
56
|
-
if (value === undefined || value === null) process.exit(1);
|
|
57
|
-
process.stdout.write(String(value));
|
|
58
|
-
} catch {
|
|
59
|
-
process.exit(1);
|
|
60
|
-
}
|
|
61
|
-
' 2>/dev/null || true)"
|
|
62
|
-
fi
|
|
63
|
-
|
|
64
|
-
[[ -n "$parsed" ]] || return 1
|
|
65
|
-
printf '%s' "$parsed"
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
safe_token() {
|
|
69
|
-
local value="$1"
|
|
70
|
-
value="$(printf '%s' "$value" | tr '[:upper:]' '[:lower:]')"
|
|
71
|
-
value="$(printf '%s' "$value" | sed -E 's/[^a-z0-9]+/-/g; s/^-+//; s/-+$//; s/-{2,}/-/g')"
|
|
72
|
-
printf '%s' "${value:-workstream}"
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
validate_block() {
|
|
76
|
-
local block="$1"
|
|
77
|
-
case "$block" in
|
|
78
|
-
""|.|..|../*|*/../*|/*|*$'\n'*|*$'\r'*)
|
|
79
|
-
return 1
|
|
80
|
-
;;
|
|
81
|
-
esac
|
|
82
|
-
[[ -e "$block" ]]
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
while [[ $# -gt 0 ]]; do
|
|
86
|
-
case "$1" in
|
|
87
|
-
--block)
|
|
88
|
-
[[ -n "${2:-}" ]] || { echo "workstream-sync: --block requires a value" >&2; exit 2; }
|
|
89
|
-
functional_block="${2#./}"
|
|
90
|
-
functional_block="${functional_block%/}"
|
|
91
|
-
shift 2
|
|
92
|
-
;;
|
|
93
|
-
--slug)
|
|
94
|
-
[[ -n "${2:-}" ]] || { echo "workstream-sync: --slug requires a value" >&2; exit 2; }
|
|
95
|
-
slug="$(safe_token "$2")"
|
|
96
|
-
shift 2
|
|
97
|
-
;;
|
|
98
|
-
--title)
|
|
99
|
-
[[ -n "${2:-}" ]] || { echo "workstream-sync: --title requires a value" >&2; exit 2; }
|
|
100
|
-
title="$2"
|
|
101
|
-
shift 2
|
|
102
|
-
;;
|
|
103
|
-
--plan)
|
|
104
|
-
[[ -n "${2:-}" ]] || { echo "workstream-sync: --plan requires a value" >&2; exit 2; }
|
|
105
|
-
source_plan="$2"
|
|
106
|
-
shift 2
|
|
107
|
-
;;
|
|
108
|
-
--slice)
|
|
109
|
-
[[ -n "${2:-}" ]] || { echo "workstream-sync: --slice requires a value" >&2; exit 2; }
|
|
110
|
-
current_slice="$2"
|
|
111
|
-
shift 2
|
|
112
|
-
;;
|
|
113
|
-
--request)
|
|
114
|
-
[[ -n "${2:-}" ]] || { echo "workstream-sync: --request requires a value" >&2; exit 2; }
|
|
115
|
-
request_file="$2"
|
|
116
|
-
shift 2
|
|
117
|
-
;;
|
|
118
|
-
--help|-h)
|
|
119
|
-
usage
|
|
120
|
-
exit 0
|
|
121
|
-
;;
|
|
122
|
-
*)
|
|
123
|
-
echo "workstream-sync: unknown argument: $1" >&2
|
|
124
|
-
usage
|
|
125
|
-
exit 2
|
|
126
|
-
;;
|
|
127
|
-
esac
|
|
128
|
-
done
|
|
129
|
-
|
|
130
|
-
if [[ "$command_name" != "ensure" ]]; then
|
|
131
|
-
echo "workstream-sync: unknown command: $command_name" >&2
|
|
132
|
-
usage
|
|
133
|
-
exit 2
|
|
134
|
-
fi
|
|
135
|
-
|
|
136
|
-
if ! validate_block "$functional_block"; then
|
|
137
|
-
echo "workstream-sync: --block must be an existing repo-relative capability prefix" >&2
|
|
138
|
-
exit 2
|
|
139
|
-
fi
|
|
140
|
-
|
|
141
|
-
if [[ ! -f "scripts/capability-resolver.ts" ]] || ! command -v bun >/dev/null 2>&1; then
|
|
142
|
-
echo "workstream-sync: scripts/capability-resolver.ts and bun are required" >&2
|
|
143
|
-
exit 2
|
|
144
|
-
fi
|
|
145
|
-
|
|
146
|
-
if ! resolver_json="$(bun scripts/capability-resolver.ts match --path "$functional_block" --format json 2>&1)"; then
|
|
147
|
-
echo "$resolver_json" >&2
|
|
148
|
-
exit 1
|
|
149
|
-
fi
|
|
150
|
-
|
|
151
|
-
if [[ "$(json_get "$resolver_json" "matched" || true)" != "true" ]]; then
|
|
152
|
-
echo "workstream-sync: --block must match a declared capability prefix in .ai/context/capabilities.json" >&2
|
|
153
|
-
exit 2
|
|
154
|
-
fi
|
|
155
|
-
|
|
156
|
-
capability_id="$(json_get "$resolver_json" "capability_id")"
|
|
157
|
-
matched_prefix="$(json_get "$resolver_json" "matched_prefix")"
|
|
158
|
-
architecture_domain="$(json_get "$resolver_json" "architecture_domain")"
|
|
159
|
-
architecture_capability="$(json_get "$resolver_json" "architecture_capability")"
|
|
160
|
-
architecture_module="$(json_get "$resolver_json" "architecture_module")"
|
|
161
|
-
workstream_dir="$(json_get "$resolver_json" "workstream_dir")"
|
|
162
|
-
contract_agents="$(json_get "$resolver_json" "contract_agents" || true)"
|
|
163
|
-
contract_claude="$(json_get "$resolver_json" "contract_claude" || true)"
|
|
164
|
-
|
|
165
|
-
if [[ -z "$slug" ]]; then
|
|
166
|
-
if [[ -n "$title" ]]; then
|
|
167
|
-
slug="$(safe_token "$title")"
|
|
168
|
-
else
|
|
169
|
-
slug="$(date '+%Y%m%d')-${architecture_capability}"
|
|
170
|
-
fi
|
|
171
|
-
fi
|
|
172
|
-
title="${title:-$slug}"
|
|
173
|
-
workstream_file="${workstream_dir}/${slug}.md"
|
|
174
|
-
event_file=".ai/harness/events.jsonl"
|
|
175
|
-
iso_timestamp="$(date '+%Y-%m-%dT%H:%M:%S%z')"
|
|
176
|
-
architecture_domain_file="docs/architecture/domains/${architecture_domain}.md"
|
|
177
|
-
|
|
178
|
-
mkdir -p "$workstream_dir" "$(dirname "$architecture_module")" "$(dirname "$architecture_domain_file")" ".ai/harness"
|
|
179
|
-
|
|
180
|
-
if [[ ! -f "$architecture_domain_file" ]]; then
|
|
181
|
-
cat > "$architecture_domain_file" <<EOF_DOMAIN
|
|
182
|
-
# Architecture Domain: ${architecture_domain}
|
|
183
|
-
|
|
184
|
-
> **Source**: \`.ai/context/capabilities.json\`
|
|
185
|
-
|
|
186
|
-
## Capabilities
|
|
187
|
-
|
|
188
|
-
- \`${capability_id}\` -> \`${architecture_module}\`
|
|
189
|
-
EOF_DOMAIN
|
|
190
|
-
elif ! grep -Fq "$architecture_module" "$architecture_domain_file"; then
|
|
191
|
-
{
|
|
192
|
-
echo ""
|
|
193
|
-
echo "- \`${capability_id}\` -> \`${architecture_module}\`"
|
|
194
|
-
} >> "$architecture_domain_file"
|
|
195
|
-
fi
|
|
196
|
-
|
|
197
|
-
if [[ ! -f "$architecture_module" ]]; then
|
|
198
|
-
cat > "$architecture_module" <<EOF_MODULE
|
|
199
|
-
# Architecture Module: ${architecture_domain}/${architecture_capability}
|
|
200
|
-
|
|
201
|
-
> **Capability ID**: \`${capability_id}\`
|
|
202
|
-
> **Functional Block**: \`${functional_block}\`
|
|
203
|
-
> **Matched Prefix**: \`${matched_prefix}\`
|
|
204
|
-
> **Domain**: \`${architecture_domain}\`
|
|
205
|
-
> **Capability**: \`${architecture_capability}\`
|
|
206
|
-
|
|
207
|
-
## Boundary
|
|
208
|
-
|
|
209
|
-
- Responsibility: Keep the capability boundary, entrypoints, runtime path, dependency rules, and local verification pointers for \`${capability_id}\`.
|
|
210
|
-
- Entrypoints: \`${matched_prefix}\`
|
|
211
|
-
- Runtime path: \`${matched_prefix}\`
|
|
212
|
-
- Local contracts: \`${contract_agents:-none}\`, \`${contract_claude:-none}\`
|
|
213
|
-
|
|
214
|
-
## Active Workstreams
|
|
215
|
-
|
|
216
|
-
- \`${workstream_file}\`
|
|
217
|
-
EOF_MODULE
|
|
218
|
-
elif ! grep -Fq "$workstream_file" "$architecture_module"; then
|
|
219
|
-
{
|
|
220
|
-
echo ""
|
|
221
|
-
echo "- \`${workstream_file}\`"
|
|
222
|
-
} >> "$architecture_module"
|
|
223
|
-
fi
|
|
224
|
-
|
|
225
|
-
if [[ ! -f "$workstream_file" ]]; then
|
|
226
|
-
cat > "$workstream_file" <<EOF_WORKSTREAM
|
|
227
|
-
# Workstream: ${title}
|
|
228
|
-
|
|
229
|
-
> **Status**: active
|
|
230
|
-
> **Capability ID**: \`${capability_id}\`
|
|
231
|
-
> **Functional Block**: \`${functional_block}\`
|
|
232
|
-
> **Matched Prefix**: \`${matched_prefix}\`
|
|
233
|
-
> **Architecture Domain**: \`${architecture_domain}\`
|
|
234
|
-
> **Architecture Capability**: \`${architecture_capability}\`
|
|
235
|
-
> **Architecture Module**: \`${architecture_module}\`
|
|
236
|
-
> **Source Plan**: ${source_plan}
|
|
237
|
-
> **Current Slice**: ${current_slice}
|
|
238
|
-
> **Last Handoff**: \`.ai/harness/session/continuation.md\`
|
|
239
|
-
> **Architecture Request**: ${request_file}
|
|
240
|
-
|
|
241
|
-
## Purpose
|
|
242
|
-
|
|
243
|
-
Track durable multi-session progress for \`${capability_id}\` without inflating local agent instructions.
|
|
244
|
-
|
|
245
|
-
## TODOs
|
|
246
|
-
|
|
247
|
-
- [ ] ${current_slice}: Complete the current executable slice for \`${capability_id}\`.
|
|
248
|
-
|
|
249
|
-
## Notes
|
|
250
|
-
|
|
251
|
-
- Project the current slice into \`tasks/todos.md\` for a single session.
|
|
252
|
-
- Keep architecture facts in \`${architecture_module}\`; keep execution progress here.
|
|
253
|
-
EOF_WORKSTREAM
|
|
254
|
-
fi
|
|
255
|
-
|
|
256
|
-
event_json="{\"ts\":\"$(json_escape "$iso_timestamp")\",\"file_path\":\"$(json_escape "$workstream_file")\",\"severity\":\"medium\",\"functional_block\":\"$(json_escape "$functional_block")\",\"capability_id\":\"$(json_escape "$capability_id")\",\"matched_prefix\":\"$(json_escape "$matched_prefix")\",\"architecture_domain\":\"$(json_escape "$architecture_domain")\",\"architecture_capability\":\"$(json_escape "$architecture_capability")\",\"architecture_module\":\"$(json_escape "$architecture_module")\",\"workstream_dir\":\"$(json_escape "$workstream_dir")\",\"contract_agents\":\"$(json_escape "$contract_agents")\",\"contract_claude\":\"$(json_escape "$contract_claude")\",\"active_workstream\":\"$(json_escape "$workstream_file")\",\"change_type\":\"workstream-sync\",\"spawn_recommended\":false,\"contract_sync_required\":true,\"request_file\":\"$(json_escape "$request_file")\"}"
|
|
257
|
-
printf '%s\n' "$event_json" >> "$event_file"
|
|
258
|
-
|
|
259
|
-
if [[ -x "scripts/context-contract-sync.sh" ]]; then
|
|
260
|
-
bash "scripts/context-contract-sync.sh" sync-event --json "$event_json"
|
|
261
|
-
fi
|
|
262
|
-
|
|
263
|
-
echo "[WorkstreamSync] Ensured $workstream_file for $capability_id."
|
|
3
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
4
|
+
exec "$SCRIPT_DIR/lib/run-forge-helper.sh" "workstream-sync.sh" "$@"
|
|
@@ -63,13 +63,17 @@ export function installerNextSteps(result:Record<string,unknown>):string[]{
|
|
|
63
63
|
.filter(Boolean)
|
|
64
64
|
.slice(0,10);
|
|
65
65
|
}
|
|
66
|
-
export function registrationFrom(
|
|
66
|
+
export function registrationFrom(
|
|
67
|
+
result:Record<string,unknown>,
|
|
68
|
+
entry:RegistryEntry,
|
|
69
|
+
options: { packageIdentityVerified?: boolean } = {},
|
|
70
|
+
):ExternalPluginRegistrationInput{
|
|
67
71
|
if(result.registration){const input=result.registration as ExternalPluginRegistrationInput;if(input.pluginId!==entry.id)throw new Error('PLUGIN_INSTALLER_ID_MISMATCH');if(input.pluginVersion!==entry.version)throw new Error('PLUGIN_INSTALLER_VERSION_MISMATCH');return input;}
|
|
68
72
|
const facts=result.providerInstall as Record<string,unknown>|undefined;
|
|
69
73
|
if(!facts||facts.kind!=='desktop_operator'||entry.id!=='desktop_operator')throw new Error('PLUGIN_INSTALLER_REGISTRATION_MISSING');
|
|
70
74
|
const expectedProviderVersion=entry.providerVersion??entry.version;
|
|
71
75
|
const expectedProtocolVersion=entry.protocolVersion??'1.0';
|
|
72
|
-
if(facts.pluginVersion!==expectedProviderVersion)throw new Error(`PLUGIN_INSTALLER_PROVIDER_VERSION_MISMATCH: expected ${expectedProviderVersion} for ${entry.id}@${entry.version}, actual ${String(facts.pluginVersion??'missing')}`);
|
|
76
|
+
if(facts.pluginVersion!==expectedProviderVersion&&!options.packageIdentityVerified)throw new Error(`PLUGIN_INSTALLER_PROVIDER_VERSION_MISMATCH: expected ${expectedProviderVersion} for ${entry.id}@${entry.version}, actual ${String(facts.pluginVersion??'missing')}`);
|
|
73
77
|
if(facts.protocolVersion!==expectedProtocolVersion)throw new Error(`PLUGIN_INSTALLER_PROTOCOL_VERSION_MISMATCH: expected ${expectedProtocolVersion} for ${entry.id}@${entry.version}, actual ${String(facts.protocolVersion??'missing')}`);
|
|
74
78
|
const socketPath=typeof facts.socketPath==='string'?facts.socketPath:'';const launchAgentLabel=typeof facts.launchAgentLabel==='string'?facts.launchAgentLabel:'';const expectedProgramContains=typeof facts.expectedProgramContains==='string'?facts.expectedProgramContains:'';
|
|
75
79
|
if(!isAbsolute(socketPath)||!launchAgentLabel||!expectedProgramContains)throw new Error('PLUGIN_INSTALLER_PROVIDER_FACTS_INVALID');
|
|
@@ -86,7 +90,12 @@ function install(entry:RegistryEntry,controllerHome:string):Record<string,unknow
|
|
|
86
90
|
const manifest=JSON.parse(readFileSync(manifestPath,'utf8')) as Record<string,unknown>;if(manifest.id!==entry.id||manifest.version!==entry.version)throw new Error(`PLUGIN_PACKAGE_IDENTITY_MISMATCH: ${entry.id}`);
|
|
87
91
|
if(existsSync(finalRoot)){renameSync(finalRoot,backup);backed=true;}renameSync(stage,finalRoot);
|
|
88
92
|
const result=installerJson(run(process.execPath,[within(finalRoot,entry.installer)],finalRoot,180000));
|
|
89
|
-
|
|
93
|
+
// The staged package manifest is checked against the catalog before the
|
|
94
|
+
// installer runs. Provider identity is then probed through the installed
|
|
95
|
+
// socket during registry sync. The installer's providerInstall version is
|
|
96
|
+
// useful diagnostics, but must not strand a verified package when that
|
|
97
|
+
// helper field is stale.
|
|
98
|
+
const installed=installExternalPluginRegistration(controllerHome,registrationFrom(result,entry,{packageIdentityVerified:true}));
|
|
90
99
|
const repository=controllerPluginRepository(controllerHome);syncAssistantPluginRegistry(controllerHome,repository);const plugin=getAssistantPluginManifest(controllerHome,repository,entry.id);
|
|
91
100
|
if(backed)rmSync(backup,{recursive:true,force:true});
|
|
92
101
|
return{pluginId:entry.id,version:entry.version,packageRoot:finalRoot,registrationRevision:installed.revision,enabled:plugin.enabled,health:plugin.health,nextSteps:installerNextSteps(result)};
|
|
@@ -773,6 +773,8 @@ export function runControllerCheck(repoRoot: string, id: string, requestedTimeou
|
|
|
773
773
|
|
|
774
774
|
export interface AsyncControllerCheckOptions {
|
|
775
775
|
snapshot?: ControllerCheckSnapshot;
|
|
776
|
+
/** Explicit isolated authority for Candidate verification child processes. */
|
|
777
|
+
isolatedControllerHome?: string;
|
|
776
778
|
requestedTimeoutMs?: number;
|
|
777
779
|
onSpawn?: (pid: number) => void;
|
|
778
780
|
subscriberId?: string;
|
|
@@ -912,12 +914,15 @@ async function executeControllerCheckAsync(
|
|
|
912
914
|
check: ControllerCheck,
|
|
913
915
|
timeoutMs: number,
|
|
914
916
|
onSpawn?: (pid: number) => void,
|
|
917
|
+
isolatedControllerHome?: string,
|
|
915
918
|
): Promise<ControllerCheckResult> {
|
|
916
919
|
const maxOutputBytes = 256 * 1024;
|
|
917
920
|
const command = [check.command[0], ...check.command.slice(1)];
|
|
921
|
+
const childEnvironment = repositoryChildProcessEnvironment();
|
|
922
|
+
if (isolatedControllerHome?.trim()) childEnvironment.FORGE_CONTROLLER_HOME = resolve(isolatedControllerHome);
|
|
918
923
|
const supervised = await runBoundedChild(check.command[0], check.command.slice(1), {
|
|
919
924
|
cwd: resolve(repoRoot, check.cwd),
|
|
920
|
-
env:
|
|
925
|
+
env: childEnvironment,
|
|
921
926
|
timeoutMs,
|
|
922
927
|
maxOutputBytes,
|
|
923
928
|
stdio: 'capture',
|
|
@@ -1050,7 +1055,7 @@ export function runControllerCheckAsync(
|
|
|
1050
1055
|
const result = await executeControllerCheckAsync(repoRoot, check, timeoutMs, (pid) => {
|
|
1051
1056
|
lease?.setChildPid(pid);
|
|
1052
1057
|
notifySpawn(pid);
|
|
1053
|
-
});
|
|
1058
|
+
}, options.isolatedControllerHome);
|
|
1054
1059
|
const completedRevision = currentControllerCheckRevision(repoRoot);
|
|
1055
1060
|
const stale = completedRevision !== revision;
|
|
1056
1061
|
const finalized = {
|
|
@@ -254,11 +254,17 @@ export function buildControllerContextPack(
|
|
|
254
254
|
? [...metadata.changedFiles.added, ...metadata.changedFiles.modified]
|
|
255
255
|
: [];
|
|
256
256
|
overlayChangedFiles = Array.from(new Set([...overlayChangedFiles, ...providerChangedFiles]));
|
|
257
|
+
const structuralEvidenceCurrent = structural.status === "ready"
|
|
258
|
+
&& baselineRevisionMatches
|
|
259
|
+
&& overlayChangedFiles.length === 0;
|
|
260
|
+
const structuralStatus = structural.status === "ready" && !structuralEvidenceCurrent
|
|
261
|
+
? "stale"
|
|
262
|
+
: structural.status;
|
|
257
263
|
structuralContext = {
|
|
258
264
|
provider: "codegraph",
|
|
259
265
|
requestedMode: structuralMode,
|
|
260
|
-
status:
|
|
261
|
-
requiredSatisfied: structuralMode !== "required" ||
|
|
266
|
+
status: structuralStatus,
|
|
267
|
+
requiredSatisfied: structuralMode !== "required" || structuralEvidenceCurrent,
|
|
262
268
|
indexSource,
|
|
263
269
|
overlayChangedFiles,
|
|
264
270
|
baselineRevisionMatches,
|
|
@@ -285,24 +291,28 @@ export function buildControllerContextPack(
|
|
|
285
291
|
const addStructuralPath = (path: string, reason: string, line?: number): void => {
|
|
286
292
|
if (!path || !coveredByGlob(path, searchIncludeGlobs) || excludeGlobs.some((glob) => globMatches(glob, path))) return;
|
|
287
293
|
const readable = readableFile(repoRoot, policy, path);
|
|
288
|
-
if (
|
|
294
|
+
if (readable.ok === false) {
|
|
289
295
|
deniedPaths.push({ path: readable.path, reason: readable.reason });
|
|
290
296
|
return;
|
|
291
297
|
}
|
|
292
298
|
addReason(candidates, readable.path, reason, line);
|
|
293
299
|
};
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
300
|
+
// Structural relationships may only influence discovery/materialization
|
|
301
|
+
// when the graph is current for this exact source identity. Stale graph
|
|
302
|
+
// nodes remain visible through structuralContext/impactContext hints, but
|
|
303
|
+
// must never steer which raw source GPT sees first.
|
|
304
|
+
if (structuralEvidenceCurrent) {
|
|
305
|
+
for (const node of graph.nodes.slice(0, maxFiles * 6)) {
|
|
306
|
+
addStructuralPath(node.filePath, `codegraph:context:${node.kind}:${node.name}`, node.startLine);
|
|
307
|
+
}
|
|
308
|
+
for (const node of graph.entryPoints) {
|
|
309
|
+
addStructuralPath(node.filePath, `codegraph:entry:${node.name}`, node.startLine);
|
|
310
|
+
}
|
|
311
|
+
for (const path of graph.relatedFiles.slice(0, maxFiles * 6)) addStructuralPath(path, "codegraph:related");
|
|
312
|
+
// Context search identifies the structural entry points. For a bounded
|
|
313
|
+
// number of those files, enrich the same Context Plane call with direct
|
|
314
|
+
// file dependencies/dependents. This is impact evidence, not a second
|
|
315
|
+
// routing/tool surface, and remains completely off for ordinary Direct.
|
|
306
316
|
const primaryFiles = Array.from(new Set(graph.entryPoints.map((node) => node.filePath).filter(Boolean))).slice(0, 4);
|
|
307
317
|
for (const filePath of primaryFiles) {
|
|
308
318
|
const adjacency = queryStructural(structuralIndexRoot, { operation: "file_dependencies", filePath, limit: 40 });
|
|
@@ -323,10 +333,13 @@ export function buildControllerContextPack(
|
|
|
323
333
|
}
|
|
324
334
|
}
|
|
325
335
|
}
|
|
326
|
-
for (const path of metadata?.changedFiles.added ?? []) addStructuralPath(path, "codegraph:changed-file", 1);
|
|
327
|
-
for (const path of metadata?.changedFiles.modified ?? []) addStructuralPath(path, "codegraph:changed-file", 1);
|
|
328
336
|
for (const path of metadata?.changedFiles.removed ?? []) omitted.push({ path, reason: "codegraph reports file removed since index" });
|
|
329
|
-
|
|
337
|
+
// A repository-baseline graph deliberately runs against another checkout.
|
|
338
|
+
// Current worktree changes are still authoritative raw-source evidence,
|
|
339
|
+
// but they come from Git rather than stale CodeGraph metadata.
|
|
340
|
+
if (indexSource === "repository_baseline") {
|
|
341
|
+
for (const path of gitStatusChangedPaths(git.status)) addStructuralPath(path, "worktree:changed-file", 1);
|
|
342
|
+
}
|
|
330
343
|
}
|
|
331
344
|
}
|
|
332
345
|
|
|
@@ -430,7 +443,15 @@ export function buildControllerContextPack(
|
|
|
430
443
|
return left.path.localeCompare(right.path);
|
|
431
444
|
});
|
|
432
445
|
const isExactKnownCandidate = (entry: { reasons: string[] }): boolean => entry.reasons.includes("explicit-known-path");
|
|
433
|
-
|
|
446
|
+
// Adaptive closure is enabled whenever the caller asks for investigation/review
|
|
447
|
+
// evidence or leaves structural discovery enabled. A deliberately narrow
|
|
448
|
+
// implementation read with structural discovery disabled stays single-wave.
|
|
449
|
+
// This keeps the common exact-file edit path cheap without making the default
|
|
450
|
+
// implementation mode a permanent evidence-closure blind spot.
|
|
451
|
+
const multiWaveEligible = retrievalMode !== "implementation"
|
|
452
|
+
|| structuralMode !== "off"
|
|
453
|
+
|| impactDomains.length > 0
|
|
454
|
+
|| explicitKnownPaths.length > 1;
|
|
434
455
|
timingsMs.expansionBudgetMax = multiWaveEligible ? Math.min(6, Math.max(2, Math.ceil(maxFiles / 2))) : 0;
|
|
435
456
|
const rankedExactKnownCount = rankedCandidates.filter(isExactKnownCandidate).length;
|
|
436
457
|
const initialCandidateLimit = multiWaveEligible
|
|
@@ -511,7 +532,7 @@ export function buildControllerContextPack(
|
|
|
511
532
|
}
|
|
512
533
|
}
|
|
513
534
|
|
|
514
|
-
//
|
|
535
|
+
// Evidence-closure eligible requests perform bounded internal waves after
|
|
515
536
|
// Wave 1 raw source materialization. Each later wave derives concrete paths
|
|
516
537
|
// from the source that was just read plus current CodeGraph adjacency, then
|
|
517
538
|
// materializes those paths in the same context request.
|
|
@@ -794,6 +815,7 @@ export function buildControllerContextPack(
|
|
|
794
815
|
...expansionSignals,
|
|
795
816
|
...(likelyRelatedNotInspected.length > 0 ? ["likely_related_files_not_inspected"] : []),
|
|
796
817
|
])).slice(0, 80);
|
|
818
|
+
const materialRelationshipUnresolved = unresolvedExpansionPaths.length > 0;
|
|
797
819
|
const readinessReasonCodes = Array.from(new Set([
|
|
798
820
|
...(files.length === 0 ? ["raw_source_unavailable"] : []),
|
|
799
821
|
...(rawSnippetTruncated ? ["raw_source_truncated"] : []),
|
|
@@ -804,9 +826,12 @@ export function buildControllerContextPack(
|
|
|
804
826
|
...(policyDeniedFiles > 0 || deniedPaths.length > 0 ? ["policy_denied_evidence"] : []),
|
|
805
827
|
...(skippedLargeFiles > 0 ? ["large_evidence_skipped"] : []),
|
|
806
828
|
...(expansionBudgetExhausted ? ["expansion_budget_exhausted"] : []),
|
|
829
|
+
...(materialRelationshipUnresolved ? ["material_relationship_unresolved"] : []),
|
|
807
830
|
...(likelyRelatedNotInspected.length > 0 ? ["related_evidence_not_materialized"] : []),
|
|
808
831
|
])).slice(0, 40);
|
|
809
|
-
const readinessStatus = files.length === 0
|
|
832
|
+
const readinessStatus = files.length === 0
|
|
833
|
+
|| (structuralMode === "required" && !structuralContext.requiredSatisfied)
|
|
834
|
+
|| materialRelationshipUnresolved
|
|
810
835
|
? "insufficient" as const
|
|
811
836
|
: readinessReasonCodes.length > 0
|
|
812
837
|
? "degraded" as const
|