archctx 0.8.0 → 0.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/assets/catalog.yaml +2 -2
- package/bin/archctx.mjs +1630 -152
- package/package.json +1 -1
- package/schemas/runtime/archctx-capabilities.schema.json +1 -1
- package/schemas/runtime/native-model-issue.schema.json +18 -0
- package/schemas/runtime/projection-pr-verification.schema.json +34 -0
- package/schemas/runtime/projection-publish-run.schema.json +74 -0
- package/schemas/runtime/projection-publish-status.schema.json +36 -0
package/bin/archctx.mjs
CHANGED
|
@@ -96,6 +96,7 @@ var init_schema = __esm(() => {
|
|
|
96
96
|
AC_NODE_VERSION_UNSUPPORTED: { code: "AC_NODE_VERSION_UNSUPPORTED", severity: "error", retryable: false, action: "use-supported-node-version" },
|
|
97
97
|
AC_CODEGRAPH_UNAVAILABLE: { code: "AC_CODEGRAPH_UNAVAILABLE", severity: "error", retryable: true, action: "run-diagnostics" },
|
|
98
98
|
AC_CODE_FACTS_UNAVAILABLE: { code: "AC_CODE_FACTS_UNAVAILABLE", severity: "error", retryable: true, action: "codegraph-init" },
|
|
99
|
+
AC_CODE_FACTS_NOT_READY: { code: "AC_CODE_FACTS_NOT_READY", severity: "error", retryable: false, action: "codegraph-index" },
|
|
99
100
|
AC_INDEX_STALE: { code: "AC_INDEX_STALE", severity: "warning", retryable: true, action: "sync-codefacts" },
|
|
100
101
|
AC_CONTEXT_STALE: { code: "AC_CONTEXT_STALE", severity: "warning", retryable: true, action: "prepare-task-again" },
|
|
101
102
|
AC_SCHEMA_INVALID: { code: "AC_SCHEMA_INVALID", severity: "error", retryable: false, action: "repair-model" },
|
|
@@ -1752,7 +1753,9 @@ var init_projection = __esm(() => {
|
|
|
1752
1753
|
"projection-preview-v1",
|
|
1753
1754
|
"projection-prior-committed-applies-v1",
|
|
1754
1755
|
"projection-protocol-v2",
|
|
1756
|
+
"projection-publish-v1",
|
|
1755
1757
|
"projection-snapshot-v1",
|
|
1758
|
+
"projection-verify-pr-v1",
|
|
1756
1759
|
"recommendation-scan-decision-v1",
|
|
1757
1760
|
"recommendation-v3",
|
|
1758
1761
|
"refactor-assessment-v1",
|
|
@@ -2031,6 +2034,23 @@ function dataRecord(value) {
|
|
|
2031
2034
|
return fields;
|
|
2032
2035
|
}
|
|
2033
2036
|
function localFactValue(rule, value) {
|
|
2037
|
+
if (rule === "codeFactsIssues") {
|
|
2038
|
+
if (!Array.isArray(value))
|
|
2039
|
+
throw new Error("Code facts issues must be a list");
|
|
2040
|
+
const length = Object.getOwnPropertyDescriptor(value, "length")?.value;
|
|
2041
|
+
if (!Number.isSafeInteger(length) || length < 1 || length > 32 || Reflect.ownKeys(value).length !== length + 1)
|
|
2042
|
+
throw new Error("Code facts issues exceed their closed budget");
|
|
2043
|
+
return Array.from({ length }, (_, index) => {
|
|
2044
|
+
const entry = Object.getOwnPropertyDescriptor(value, String(index));
|
|
2045
|
+
if (!entry || !("value" in entry))
|
|
2046
|
+
throw new Error("Code facts issues must use own data");
|
|
2047
|
+
const fields = dataRecord(entry.value);
|
|
2048
|
+
const code = fields.code?.value, message = fields.message?.value;
|
|
2049
|
+
if (Object.keys(fields).length !== 2 || typeof code !== "string" || !Object.hasOwn(CODE_FACTS_ISSUE_MESSAGES, code) || message !== CODE_FACTS_ISSUE_MESSAGES[code])
|
|
2050
|
+
throw new Error("Unknown code facts diagnostic");
|
|
2051
|
+
return { code, message };
|
|
2052
|
+
});
|
|
2053
|
+
}
|
|
2034
2054
|
if (rule === "commandSuggestion" && (value === null || typeof value === "string" && bytes(value) <= 1024 && /^archctx (recommendations |refactor verify )[^\r\n]*$/.test(value)))
|
|
2035
2055
|
return value;
|
|
2036
2056
|
if (rule === "publicConfirmationToken" && typeof value === "string" && bytes(value) <= 1024 && /^public:github\.com\/[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+:[a-f0-9]{40}:[A-Za-z0-9_.:-]+$/.test(value))
|
|
@@ -2130,7 +2150,7 @@ function nativeFactsFor(policy, input) {
|
|
|
2130
2150
|
}
|
|
2131
2151
|
const item = dataRecord(value);
|
|
2132
2152
|
if (type === "issues") {
|
|
2133
|
-
if (
|
|
2153
|
+
if (!["entityId", "field", "invariant"].every((key) => Object.hasOwn(item, key)) || Object.keys(item).some((key) => !["entityId", "field", "invariant", "path"].includes(key)))
|
|
2134
2154
|
throw new Error("Native issue schema is invalid");
|
|
2135
2155
|
const clean = Object.fromEntries(Object.entries(item).map(([key, data]) => [key, text(data.value)]));
|
|
2136
2156
|
if (Object.values(clean).some((v) => bytes(v) > 256)) {
|
|
@@ -2202,6 +2222,8 @@ function nativeFactsFor(policy, input) {
|
|
|
2202
2222
|
}
|
|
2203
2223
|
result[key] = values;
|
|
2204
2224
|
}
|
|
2225
|
+
const sourceIssues = Array.isArray(result.issues) ? result.issues : [];
|
|
2226
|
+
const summarizePath = sourceIssues.some((issue) => typeof issue.path === "string");
|
|
2205
2227
|
const counts = () => {
|
|
2206
2228
|
if (!Number.isSafeInteger(omitted) || !Number.isSafeInteger(characters))
|
|
2207
2229
|
throw new Error("Native diagnostic counts exceed schema");
|
|
@@ -2211,6 +2233,13 @@ function nativeFactsFor(policy, input) {
|
|
|
2211
2233
|
result.omittedDiagnosticCharacters = characters;
|
|
2212
2234
|
};
|
|
2213
2235
|
const fits = () => {
|
|
2236
|
+
if (summarizePath) {
|
|
2237
|
+
const path = sourceIssues[0]?.path;
|
|
2238
|
+
if (typeof path === "string" && sourceIssues.every((issue) => issue.path === path))
|
|
2239
|
+
result.path = path;
|
|
2240
|
+
else
|
|
2241
|
+
delete result.path;
|
|
2242
|
+
}
|
|
2214
2243
|
counts();
|
|
2215
2244
|
const base = catalogDefaults[policy.code];
|
|
2216
2245
|
const error = { code: policy.code, message: policy.message, severity: base.severity, retryable: policy.retryable ?? base.retryable, action: policy.action ?? base.action, reasonCode: Object.entries(policies).find(([, p]) => p === policy)?.[0], details: result };
|
|
@@ -2325,12 +2354,21 @@ function normalizeFailureResult(result, stage, requestId = "failure") {
|
|
|
2325
2354
|
return toEnvelope(requestId, undefined, "failure-metadata");
|
|
2326
2355
|
}
|
|
2327
2356
|
}
|
|
2328
|
-
var FAILURE_STAGES, policies, FAILURE_POLICIES, catalogDefaults, actions, bytes = (value) => new TextEncoder().encode(value).byteLength, record2 = (value) => value !== null && typeof value === "object" && !Array.isArray(value), finiteNonnegative = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0, criticalKey = (key) => /id$|digest|receipt|binding|readback|lookupkey/i.test(key), localEnums, nativeEnums, nativeDiagnosticKinds, FailureError;
|
|
2357
|
+
var FAILURE_STAGES, CODE_FACTS_ISSUE_MESSAGES, policies, FAILURE_POLICIES, catalogDefaults, actions, bytes = (value) => new TextEncoder().encode(value).byteLength, record2 = (value) => value !== null && typeof value === "object" && !Array.isArray(value), finiteNonnegative = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0, criticalKey = (key) => /id$|digest|receipt|binding|readback|lookupkey/i.test(key), localEnums, nativeEnums, nativeDiagnosticKinds, FailureError;
|
|
2329
2358
|
var init_failure = __esm(() => {
|
|
2330
2359
|
init_changeset_recovery();
|
|
2331
2360
|
init_projection_recovery();
|
|
2332
2361
|
init_schema();
|
|
2333
2362
|
FAILURE_STAGES = Object.freeze(["debt", "complete", "practice-checkpoint", "cli", "mcp", "hook-checkpoint", "failure-metadata", "public-operation", "native-export", "docs-anchors", "flows-check"]);
|
|
2363
|
+
CODE_FACTS_ISSUE_MESSAGES = Object.freeze({
|
|
2364
|
+
"not-initialized": "CodeGraph repository status is not initialized",
|
|
2365
|
+
"version-mismatch": "CodeGraph repository status version mismatch",
|
|
2366
|
+
"project-path-mismatch": "CodeGraph repository project path mismatch",
|
|
2367
|
+
"index-incomplete": "CodeGraph repository index is not complete and compatible",
|
|
2368
|
+
"extraction-version-mismatch": "CodeGraph repository extraction version mismatch",
|
|
2369
|
+
"worktree-mismatch": "CodeGraph repository worktree mismatch",
|
|
2370
|
+
"pending-source-changes": "CodeGraph repository index has pending or unreported source changes"
|
|
2371
|
+
});
|
|
2334
2372
|
policies = {
|
|
2335
2373
|
"git-worktree-required": { code: "AC_REPO_NOT_FOUND", message: "This operation requires a Git worktree.", fields: ["checks"] },
|
|
2336
2374
|
"model-missing": { code: "AC_MODEL_ADOPTION_REQUIRED", message: "Architecture debt requires an adopted model.", fields: ["checks"] },
|
|
@@ -2340,6 +2378,7 @@ var init_failure = __esm(() => {
|
|
|
2340
2378
|
"projection-manifest-invalid": { code: "AC_PRECONDITION_FAILED", message: "The committed projection manifest violates its contract." },
|
|
2341
2379
|
"projection-footprint-uncovered": { code: "AC_PRECONDITION_FAILED", message: "Declared source footprints lack a projection target." },
|
|
2342
2380
|
"index-missing": { code: "AC_CODE_FACTS_UNAVAILABLE", message: "Required code facts have no available index. Run codegraph init." },
|
|
2381
|
+
"code-facts-not-ready": { code: "AC_CODE_FACTS_NOT_READY", message: "Repository code facts are not ready. Run archctx doctor --json and follow the reported action.", facts: { issues: "codeFactsIssues" } },
|
|
2343
2382
|
"checkpoint-input-invalid": { code: "AC_SCHEMA_INVALID", message: "Checkpoint arguments violate the declared input schema.", fields: ["issueCount"], action: "correct-command-arguments" },
|
|
2344
2383
|
"cli-input-invalid": { code: "AC_SCHEMA_INVALID", message: "Command arguments violate the declared input contract.", action: "correct-command-arguments" },
|
|
2345
2384
|
"complete-input-invalid": { code: "AC_SCHEMA_INVALID", message: "Completion input violates the caller authority contract.", action: "correct-command-arguments" },
|
|
@@ -2348,7 +2387,7 @@ var init_failure = __esm(() => {
|
|
|
2348
2387
|
"runtime-version-unsupported": { code: "AC_RUNTIME_VERSION_UNSUPPORTED", message: "The connected runtime version is unsupported." },
|
|
2349
2388
|
"rpc-timeout": { code: "RPC_TIMEOUT", message: "The runtime RPC deadline elapsed without a response; a mutation outcome is unknown.", fields: ["method", "timeoutMs", "elapsedMs", "mutationOutcome"], action: "reconcile-before-retry" },
|
|
2350
2389
|
"rpc-aborted": { code: "RPC_ABORTED", message: "The runtime RPC caller aborted without a response; a mutation outcome is unknown.", fields: ["method", "timeoutMs", "elapsedMs", "mutationOutcome"], action: "reconcile-before-retry" },
|
|
2351
|
-
"failure-unclassified": { code: "AC_UNCLASSIFIED_FAILURE", message: "The operation failed without a classified cause.
|
|
2390
|
+
"failure-unclassified": { code: "AC_UNCLASSIFIED_FAILURE", message: "The operation failed without a classified cause. Run archctx doctor --json before retrying.", action: "inspect-diagnostics" }
|
|
2352
2391
|
};
|
|
2353
2392
|
Object.assign(policies, {
|
|
2354
2393
|
"developer-review-run-not-owned": { code: "AC_PRECONDITION_FAILED", message: "developer-review-run-not-owned", facts: { refusal: "reviewOwnershipRefusal" } },
|
|
@@ -2585,7 +2624,7 @@ var init_failure = __esm(() => {
|
|
|
2585
2624
|
}
|
|
2586
2625
|
FAILURE_POLICIES = Object.freeze(policies);
|
|
2587
2626
|
catalogDefaults = Object.fromEntries(Object.entries(ERROR_CATALOG).map(([code, value]) => [code, Object.freeze({ severity: value.severity, retryable: value.retryable, action: value.action })]));
|
|
2588
|
-
actions = new Set([...Object.values(catalogDefaults).map((value) => value.action), "correct-command-arguments", "inspect-diagnostics", "reconcile-before-retry", ...Object.values(policies).flatMap((policy) => policy.action ? [policy.action] : [])]);
|
|
2627
|
+
actions = new Set([...Object.values(catalogDefaults).map((value) => value.action), "correct-command-arguments", "inspect-diagnostics", "reconcile-before-retry", "install-compatible-codegraph", ...Object.values(policies).flatMap((policy) => policy.action ? [policy.action] : [])]);
|
|
2589
2628
|
localEnums = {
|
|
2590
2629
|
reviewWorktreeRefusal: ["UNKNOWN", "HEAD_UNAVAILABLE", "HEAD_SHA_MISMATCH", "TREE_OID_MISMATCH", "WORKTREE_NOT_DETACHED", "WORKTREE_NOT_CLEAN"],
|
|
2591
2630
|
reviewOwnershipRefusal: ["persisted-manifest-missing", "persisted-manifest-identity-mismatch", "run-id", "manifest-path", "lock-path", "persisted-manifest-mismatch", "persisted-lock-mismatch", "run-root-name", "worktree-temp-root", "codegraph-temporary-state-root", "run-root-not-a-directory", "run-root-owner-marker-missing", "run-root-owner-marker-mismatch", "worktree-temporary-root", "worktree-root", "worktree-source-root"],
|
|
@@ -2992,9 +3031,13 @@ function nativeAllowedNodeKinds(schemas) {
|
|
|
2992
3031
|
throw new Error("canonical-node-kind-enum-unavailable");
|
|
2993
3032
|
return [...kinds];
|
|
2994
3033
|
}
|
|
2995
|
-
function nativeContextModelIssues(model, schemas) {
|
|
3034
|
+
function nativeContextModelIssues(model, schemas, recordPaths) {
|
|
2996
3035
|
const issues = [];
|
|
2997
|
-
const add = (
|
|
3036
|
+
const add = (record, field, invariant) => {
|
|
3037
|
+
const entityId = typeof record === "string" ? record : typeof record.id === "string" ? record.id : "(undeclared)";
|
|
3038
|
+
const path = typeof record === "string" ? undefined : recordPaths?.get(record);
|
|
3039
|
+
issues.push({ entityId, field, invariant, ...path === undefined ? {} : { path } });
|
|
3040
|
+
};
|
|
2998
3041
|
const resolveSchema = nativeContextSchemaResolver(schemas);
|
|
2999
3042
|
const ids = new Set;
|
|
3000
3043
|
for (const [kind, records] of [["node", model.nodes], ["relation", model.relations], ["flow", model.flows]]) {
|
|
@@ -3013,14 +3056,14 @@ function nativeContextModelIssues(model, schemas) {
|
|
|
3013
3056
|
continue;
|
|
3014
3057
|
}
|
|
3015
3058
|
if (typeof value === "number" && !Number.isFinite(value)) {
|
|
3016
|
-
add(
|
|
3059
|
+
add(record, item.field, "non-json-number");
|
|
3017
3060
|
jsonInvalid = true;
|
|
3018
3061
|
break;
|
|
3019
3062
|
}
|
|
3020
3063
|
if (value === null || typeof value !== "object")
|
|
3021
3064
|
continue;
|
|
3022
3065
|
if (active.has(value)) {
|
|
3023
|
-
add(
|
|
3066
|
+
add(record, item.field, "non-json-cycle");
|
|
3024
3067
|
jsonInvalid = true;
|
|
3025
3068
|
break;
|
|
3026
3069
|
}
|
|
@@ -3028,7 +3071,7 @@ function nativeContextModelIssues(model, schemas) {
|
|
|
3028
3071
|
continue;
|
|
3029
3072
|
const prototype = Object.getPrototypeOf(value);
|
|
3030
3073
|
if (Array.isArray(value) ? prototype !== Array.prototype : prototype !== Object.prototype) {
|
|
3031
|
-
add(
|
|
3074
|
+
add(record, item.field, "non-json-prototype");
|
|
3032
3075
|
jsonInvalid = true;
|
|
3033
3076
|
break;
|
|
3034
3077
|
}
|
|
@@ -3041,36 +3084,35 @@ function nativeContextModelIssues(model, schemas) {
|
|
|
3041
3084
|
continue;
|
|
3042
3085
|
const schema = kind === "node" ? schemas.node : kind === "relation" ? schemas.relation : schemas.flow;
|
|
3043
3086
|
for (const issue of validateJsonSchema(schema, record, { resolveSchema }).issues)
|
|
3044
|
-
add(
|
|
3087
|
+
add(record, issue.path, issue.message);
|
|
3045
3088
|
if (ids.has(id))
|
|
3046
|
-
add(
|
|
3089
|
+
add(record, "id", "duplicate-entity-id");
|
|
3047
3090
|
ids.add(id);
|
|
3048
3091
|
}
|
|
3049
3092
|
}
|
|
3050
3093
|
if (issues.length > 0)
|
|
3051
3094
|
return issues;
|
|
3052
3095
|
for (const kind of ["nodes", "relations", "flows"]) {
|
|
3053
|
-
const expected = new
|
|
3054
|
-
for (const id of expected)
|
|
3096
|
+
const expected = new Map(model[kind].map((entity) => [entity.id, entity]));
|
|
3097
|
+
for (const [id, entity] of expected)
|
|
3055
3098
|
if (!Object.hasOwn(model.sourcePaths[kind], id))
|
|
3056
|
-
add(
|
|
3099
|
+
add(entity, `sourcePaths.${kind}`, "source-path-index-incomplete");
|
|
3057
3100
|
for (const [id, path] of Object.entries(model.sourcePaths[kind])) {
|
|
3058
3101
|
if (!expected.has(id))
|
|
3059
3102
|
add(id, `sourcePaths.${kind}`, "source-path-index-unknown-entity");
|
|
3060
3103
|
if (!isNativeContextPath(path))
|
|
3061
|
-
add(id, `sourcePaths.${kind}`, "path-unsafe");
|
|
3104
|
+
add(expected.get(id) ?? id, `sourcePaths.${kind}`, "path-unsafe");
|
|
3062
3105
|
}
|
|
3063
3106
|
}
|
|
3064
3107
|
const nodes = new Map(model.nodes.map((node) => [node.id, node]));
|
|
3065
3108
|
const checked = new Set;
|
|
3066
3109
|
for (const node of model.nodes) {
|
|
3067
|
-
const id = node.id;
|
|
3068
3110
|
const chain = new Set;
|
|
3069
3111
|
let cursor = node;
|
|
3070
3112
|
while (cursor) {
|
|
3071
3113
|
const current = cursor.id;
|
|
3072
3114
|
if (chain.has(current)) {
|
|
3073
|
-
add(
|
|
3115
|
+
add(node, "parent", "parent-cycle");
|
|
3074
3116
|
break;
|
|
3075
3117
|
}
|
|
3076
3118
|
if (checked.has(current))
|
|
@@ -3080,7 +3122,7 @@ function nativeContextModelIssues(model, schemas) {
|
|
|
3080
3122
|
break;
|
|
3081
3123
|
const parent = nodes.get(cursor.parent);
|
|
3082
3124
|
if (!parent) {
|
|
3083
|
-
add(
|
|
3125
|
+
add(cursor, "parent", "parent-node-missing");
|
|
3084
3126
|
break;
|
|
3085
3127
|
}
|
|
3086
3128
|
cursor = parent;
|
|
@@ -3093,17 +3135,17 @@ function nativeContextModelIssues(model, schemas) {
|
|
|
3093
3135
|
const source = node.source;
|
|
3094
3136
|
for (const path of [...source?.include ?? [], ...source?.exclude ?? []])
|
|
3095
3137
|
if (!isNativeContextPath(path))
|
|
3096
|
-
add(node
|
|
3138
|
+
add(node, "source", "path-unsafe");
|
|
3097
3139
|
for (const entry of source?.entrypoints ?? []) {
|
|
3098
3140
|
if (!isNativeContextPath(entry.path))
|
|
3099
|
-
add(node
|
|
3141
|
+
add(node, "source.entrypoints.path", "path-unsafe");
|
|
3100
3142
|
for (const symbol of entry.symbols) {
|
|
3101
3143
|
for (const sink of symbol.sinks) {
|
|
3102
3144
|
if (!isNativeContextPath(sink.path))
|
|
3103
|
-
add(node
|
|
3145
|
+
add(node, "source.entrypoints.symbols.sinks.path", "path-unsafe");
|
|
3104
3146
|
const selector = JSON.stringify([entry.id, symbol.name, sink.id]);
|
|
3105
3147
|
if (selectors.has(selector))
|
|
3106
|
-
add(node
|
|
3148
|
+
add(node, "source.entrypoints.symbols.sinks.id", "selector-ambiguous");
|
|
3107
3149
|
selectors.add(selector);
|
|
3108
3150
|
}
|
|
3109
3151
|
}
|
|
@@ -3112,43 +3154,42 @@ function nativeContextModelIssues(model, schemas) {
|
|
|
3112
3154
|
for (const relation of model.relations) {
|
|
3113
3155
|
for (const field of ["source", "target", "via"])
|
|
3114
3156
|
if (relation[field] !== undefined && !nodes.has(relation[field]))
|
|
3115
|
-
add(relation
|
|
3157
|
+
add(relation, field, "relation-node-missing");
|
|
3116
3158
|
}
|
|
3117
3159
|
for (const flow of model.flows) {
|
|
3118
|
-
const id = flow.id;
|
|
3119
3160
|
if (nodes.get(flow.capabilityId)?.kind !== "capability")
|
|
3120
|
-
add(
|
|
3161
|
+
add(flow, "capabilityId", "flow-capability-missing");
|
|
3121
3162
|
if (flow.applicability !== "required")
|
|
3122
3163
|
continue;
|
|
3123
3164
|
const participants = flow.participants;
|
|
3124
3165
|
const participantIds = new Set;
|
|
3125
3166
|
for (const participant of participants) {
|
|
3126
3167
|
if (participantIds.has(participant.id))
|
|
3127
|
-
add(
|
|
3168
|
+
add(flow, "participants.id", "participant-id-ambiguous");
|
|
3128
3169
|
participantIds.add(participant.id);
|
|
3129
3170
|
if (!nodes.has(participant.nodeId))
|
|
3130
|
-
add(
|
|
3171
|
+
add(flow, "participants.nodeId", "participant-node-missing");
|
|
3131
3172
|
}
|
|
3132
3173
|
const definitions = new Map;
|
|
3133
3174
|
const outcomes = flow.outcomes;
|
|
3134
3175
|
const outcomeIds = new Set;
|
|
3135
3176
|
for (const outcome of outcomes) {
|
|
3136
3177
|
if (outcomeIds.has(outcome.id))
|
|
3137
|
-
add(
|
|
3178
|
+
add(flow, "outcomes.id", "outcome-id-ambiguous");
|
|
3138
3179
|
outcomeIds.add(outcome.id);
|
|
3139
3180
|
if (!participantIds.has(outcome.terminal.participant))
|
|
3140
|
-
add(
|
|
3181
|
+
add(flow, "outcomes.terminal", "terminal-participant-missing");
|
|
3141
3182
|
}
|
|
3142
3183
|
for (const step of [...flow.steps, ...outcomes.flatMap((outcome) => outcome.steps)]) {
|
|
3143
3184
|
if (!participantIds.has(step.from) || !participantIds.has(step.to))
|
|
3144
|
-
add(
|
|
3185
|
+
add(flow, "steps", "step-participant-missing");
|
|
3145
3186
|
const definition = canonicalize(step);
|
|
3146
3187
|
if (definitions.has(step.id) && definitions.get(step.id) !== definition)
|
|
3147
|
-
add(
|
|
3188
|
+
add(flow, "steps.id", "step-definition-ambiguous");
|
|
3148
3189
|
definitions.set(step.id, definition);
|
|
3149
3190
|
const selector = step.evidence;
|
|
3150
3191
|
if (!selectors.has(JSON.stringify([selector.entrypointId, selector.sourceSymbol, selector.sinkId])))
|
|
3151
|
-
add(
|
|
3192
|
+
add(flow, "steps.evidence", "selector-missing");
|
|
3152
3193
|
}
|
|
3153
3194
|
}
|
|
3154
3195
|
return issues;
|
|
@@ -3324,6 +3365,10 @@ var init_ports = __esm(() => {
|
|
|
3324
3365
|
// packages/contracts/src/practices.ts
|
|
3325
3366
|
var PRACTICE_SCHEMA_VERSION = "archcontext.practice/v1", PRACTICE_PROFILE_SCHEMA_VERSION = "archcontext.practice-profile/v1", PRACTICE_SOURCE_SCHEMA_VERSION = "archcontext.practice-source/v1", PRACTICE_CATALOG_MANIFEST_SCHEMA_VERSION = "archcontext.practice-catalog-manifest/v1", PRACTICE_ENFORCEMENT_POLICY_SCHEMA_VERSION = "archcontext.practice-enforcement-policy/v1", PRACTICE_WAIVER_SCHEMA_VERSION = "archcontext.practice-waiver/v1";
|
|
3326
3367
|
|
|
3368
|
+
// packages/contracts/src/projection-writer.ts
|
|
3369
|
+
var PROJECTION_PUBLISH_RUN_SCHEMA_VERSION = "archcontext.projection-publish-run/v1", PROJECTION_PUBLISH_STATUS_SCHEMA_VERSION = "archcontext.projection-publish-status/v1", PROJECTION_PR_VERIFICATION_SCHEMA_VERSION = "archcontext.projection-pr-verification/v1", PROJECTION_WRITER_BRANCH_PREFIX = "archctx/", PROJECTION_WRITER_DEFAULT_BRANCH = "archctx/projection";
|
|
3370
|
+
var init_projection_writer = () => {};
|
|
3371
|
+
|
|
3327
3372
|
// packages/contracts/src/projection-manifest.ts
|
|
3328
3373
|
function architectureDocsProjectionManifestIssues(value) {
|
|
3329
3374
|
const issues = [];
|
|
@@ -3828,7 +3873,7 @@ function compareVersionTriples(left, right) {
|
|
|
3828
3873
|
}
|
|
3829
3874
|
return 0;
|
|
3830
3875
|
}
|
|
3831
|
-
var ARCHCONTEXT_PRODUCT_NAME = "archctx", ARCHCONTEXT_PRODUCT_VERSION = "0.
|
|
3876
|
+
var ARCHCONTEXT_PRODUCT_NAME = "archctx", ARCHCONTEXT_PRODUCT_VERSION = "0.9.0", ARCHCONTEXT_PACKAGE_MANAGER = "bun@1.4.3", ARCHCONTEXT_NODE_RANGE = ">=22.22 <26", LOCAL_RUNTIME_RPC_SCHEMA_VERSION = "archcontext.runtime-rpc/v2", ARCHCONTEXT_SCHEMA_SET_VERSION = "2026-07-11.explorer-view-v2";
|
|
3832
3877
|
|
|
3833
3878
|
// packages/contracts/src/refactor.ts
|
|
3834
3879
|
function refactorNamespaceIssues(value, label = "product.id") {
|
|
@@ -4930,6 +4975,7 @@ var init_src = __esm(() => {
|
|
|
4930
4975
|
init_ports();
|
|
4931
4976
|
init_projection();
|
|
4932
4977
|
init_projection_recovery();
|
|
4978
|
+
init_projection_writer();
|
|
4933
4979
|
init_projection_manifest();
|
|
4934
4980
|
init_refactor();
|
|
4935
4981
|
init_schema();
|
|
@@ -15338,7 +15384,7 @@ var init_src6 = __esm(() => {
|
|
|
15338
15384
|
|
|
15339
15385
|
// packages/local-runtime/egress-admission/src/index.ts
|
|
15340
15386
|
var LOCAL_EGRESS_MODE_ENV = "ARCHCONTEXT_EGRESS_MODE";
|
|
15341
|
-
var LOCAL_EGRESS_CHANNELS = ["context7", "agent-audit", "github-issue-publishing", "npm-update-check", "codegraph-telemetry"];
|
|
15387
|
+
var LOCAL_EGRESS_CHANNELS = ["context7", "agent-audit", "github-issue-publishing", "npm-update-check", "projection-publishing", "codegraph-telemetry"];
|
|
15342
15388
|
|
|
15343
15389
|
class LocalEgressPolicyError extends Error {
|
|
15344
15390
|
}
|
|
@@ -15748,10 +15794,10 @@ function sourceIncludeAnchors(patterns) {
|
|
|
15748
15794
|
init_src();
|
|
15749
15795
|
|
|
15750
15796
|
// packages/core/architecture-domain/src/index.ts
|
|
15751
|
-
init_src();
|
|
15752
15797
|
import { createHash as createHash2 } from "node:crypto";
|
|
15753
15798
|
import { readFileSync, realpathSync, statSync } from "node:fs";
|
|
15754
15799
|
import { resolve } from "node:path";
|
|
15800
|
+
init_src();
|
|
15755
15801
|
var LANDSCAPE_FILE = ".archcontext/landscape.yaml";
|
|
15756
15802
|
var ADAPTER_PROTECTED_NATIVE_FIELDS = ["evidence", "verification", "constraint", "intervention"];
|
|
15757
15803
|
var DEFAULT_IGNORES = new Set([
|
|
@@ -15980,7 +16026,7 @@ function parseJsonOrStableYaml(body, path) {
|
|
|
15980
16026
|
if (!trimmed)
|
|
15981
16027
|
throw new Error(`${path}: empty model file`);
|
|
15982
16028
|
if (trimmed.startsWith("{") || trimmed.startsWith("["))
|
|
15983
|
-
return
|
|
16029
|
+
return checkedJsonOrFlowYaml(trimmed, path, "json-document");
|
|
15984
16030
|
return new StableYamlParser(body, path).parse();
|
|
15985
16031
|
}
|
|
15986
16032
|
var ADR_FILE_PATH = /^docs\/adr\/ADR-\d{4}-.+\.md$/;
|
|
@@ -16361,7 +16407,7 @@ function parseScalar(value, path) {
|
|
|
16361
16407
|
if (/^-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?$/.test(value))
|
|
16362
16408
|
return Number(value);
|
|
16363
16409
|
if (/^["[{]/.test(value))
|
|
16364
|
-
return
|
|
16410
|
+
return checkedJsonOrFlowYaml(value, path, "json-scalar");
|
|
16365
16411
|
return value;
|
|
16366
16412
|
}
|
|
16367
16413
|
|
|
@@ -16379,6 +16425,54 @@ class DuplicateModelKeyError extends Error {
|
|
|
16379
16425
|
this.name = "DuplicateModelKeyError";
|
|
16380
16426
|
}
|
|
16381
16427
|
}
|
|
16428
|
+
function checkedJsonOrFlowYaml(source, path, branch) {
|
|
16429
|
+
try {
|
|
16430
|
+
return checkedJsonDecode(source, path, branch);
|
|
16431
|
+
} catch (error) {
|
|
16432
|
+
if (!(error instanceof SyntaxError) || !(source.startsWith("{") || source.startsWith("[")))
|
|
16433
|
+
throw error;
|
|
16434
|
+
}
|
|
16435
|
+
return checkedFlowYamlDecode(source, path);
|
|
16436
|
+
}
|
|
16437
|
+
function checkedFlowYamlDecode(source, path) {
|
|
16438
|
+
const document = $parseDocument(source, { version: "1.2", schema: "core", uniqueKeys: false, prettyErrors: false });
|
|
16439
|
+
const invalid = (reason) => {
|
|
16440
|
+
throw new SyntaxError(`${path}: invalid YAML flow collection (${reason})`);
|
|
16441
|
+
};
|
|
16442
|
+
if (document.errors.length || document.warnings.length)
|
|
16443
|
+
invalid("parse error or warning");
|
|
16444
|
+
if (!$isMap(document.contents) && !$isSeq(document.contents) || !document.contents.flow)
|
|
16445
|
+
invalid("expected flow collection");
|
|
16446
|
+
const decode = (node) => {
|
|
16447
|
+
if (node === null)
|
|
16448
|
+
return null;
|
|
16449
|
+
if (!$isScalar(node) && !$isMap(node) && !$isSeq(node))
|
|
16450
|
+
return invalid("unsupported node or alias");
|
|
16451
|
+
if (node.anchor || node.tag)
|
|
16452
|
+
return invalid("anchor or explicit tag");
|
|
16453
|
+
if ($isScalar(node)) {
|
|
16454
|
+
const value = node.value;
|
|
16455
|
+
if (value === null || typeof value === "string" || typeof value === "boolean")
|
|
16456
|
+
return value;
|
|
16457
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
16458
|
+
return value;
|
|
16459
|
+
return invalid("non-JSON scalar");
|
|
16460
|
+
}
|
|
16461
|
+
if ($isSeq(node))
|
|
16462
|
+
return node.items.map(decode);
|
|
16463
|
+
const keys = new Set;
|
|
16464
|
+
return Object.fromEntries(node.items.map((pair) => {
|
|
16465
|
+
if (!$isScalar(pair.key))
|
|
16466
|
+
return invalid("non-scalar key");
|
|
16467
|
+
const key = String(decode(pair.key));
|
|
16468
|
+
if (keys.has(key))
|
|
16469
|
+
throw new DuplicateModelKeyError(path, "yaml-flow", key, pair.key.range[0]);
|
|
16470
|
+
keys.add(key);
|
|
16471
|
+
return [key, decode(pair.value)];
|
|
16472
|
+
}));
|
|
16473
|
+
};
|
|
16474
|
+
return decode(document.contents);
|
|
16475
|
+
}
|
|
16382
16476
|
function checkedJsonDecode(source, path, branch) {
|
|
16383
16477
|
let offset = 0;
|
|
16384
16478
|
const whitespace = () => {
|
|
@@ -16968,6 +17062,12 @@ function renderAdrReferenceUpdate(body, references) {
|
|
|
16968
17062
|
return rendered;
|
|
16969
17063
|
}
|
|
16970
17064
|
// packages/core/changeset-engine/src/index.ts
|
|
17065
|
+
function changeSetBaseModelBlockingErrors(result, draft) {
|
|
17066
|
+
const repairedPaths = new Set(draft.operations.filter((operation) => operation.op === "update_entity_fields" || operation.op === "delete_entity").map((operation) => operation.path));
|
|
17067
|
+
const repairedErrors = new Set((result.nodeSchemaErrors ?? []).filter((issue) => repairedPaths.has(issue.path)).map((issue) => issue.error));
|
|
17068
|
+
return baseModelBlockingErrors(result).filter((error) => !repairedErrors.has(error));
|
|
17069
|
+
}
|
|
17070
|
+
|
|
16971
17071
|
class ChangeSetEngine {
|
|
16972
17072
|
deps;
|
|
16973
17073
|
states = new Map;
|
|
@@ -17190,10 +17290,16 @@ class ChangeSetEngine {
|
|
|
17190
17290
|
}
|
|
17191
17291
|
async validateModel(root, draft, deps, phase) {
|
|
17192
17292
|
const result = await deps.modelStore.validateModel({ root, repositoryId: draft.reason.taskSessionId, headSha: draft.base.headSha });
|
|
17193
|
-
const
|
|
17293
|
+
const errors = phase === "before" ? changeSetBaseModelBlockingErrors(result, draft) : result.errors;
|
|
17294
|
+
const blocked = phase === "before" ? errors.length > 0 : !result.valid;
|
|
17194
17295
|
if (blocked) {
|
|
17195
|
-
const
|
|
17196
|
-
|
|
17296
|
+
const path = errors[0]?.match(/^(\.archcontext\/.+?): /)?.[1];
|
|
17297
|
+
const error = new FailureError("AC_SCHEMA_INVALID", "native-context-model-invalid", {
|
|
17298
|
+
invariant: `model-validation-${phase}-apply`,
|
|
17299
|
+
...path ? { path } : {}
|
|
17300
|
+
});
|
|
17301
|
+
error.message = `ChangeSet model validation failed ${phase} apply: ${errors.join("; ") || "unknown validation error"}`;
|
|
17302
|
+
throw error;
|
|
17197
17303
|
}
|
|
17198
17304
|
}
|
|
17199
17305
|
requireDeps() {
|
|
@@ -20916,6 +21022,16 @@ ${wrapped}`;
|
|
|
20916
21022
|
}
|
|
20917
21023
|
return `${existing.slice(0, region.start)}${wrapped}${existing.slice(region.end)}`;
|
|
20918
21024
|
}
|
|
21025
|
+
function agentContextTextOutsideRegions(body, nodeIds) {
|
|
21026
|
+
let text = body;
|
|
21027
|
+
for (const nodeId of [...new Set(nodeIds)].sort()) {
|
|
21028
|
+
const region = findAgentContextRegion(text, nodeId);
|
|
21029
|
+
if (!region)
|
|
21030
|
+
throw new Error(`agent-context-marker-missing: ${nodeId}`);
|
|
21031
|
+
text = `${text.slice(0, region.start)}\x00archcontext-agent-context-region:${nodeId}\x00${text.slice(region.end)}`;
|
|
21032
|
+
}
|
|
21033
|
+
return text;
|
|
21034
|
+
}
|
|
20919
21035
|
function findAgentContextRegion(body, nodeId) {
|
|
20920
21036
|
const startPattern = new RegExp(`<!-- BEGIN ARCHCONTEXT AGENT CONTEXT id="${escapeRegExp(nodeId)}"[^>]*-->`);
|
|
20921
21037
|
const startMatch = startPattern.exec(body);
|
|
@@ -22897,12 +23013,9 @@ function isGitWorktreeError(error) {
|
|
|
22897
23013
|
return error instanceof Error && /git|Command failed/.test(error.message);
|
|
22898
23014
|
}
|
|
22899
23015
|
|
|
22900
|
-
// packages/local-runtime/runtime-daemon/src/producer-failure.ts
|
|
22901
|
-
init_src();
|
|
22902
|
-
|
|
22903
23016
|
// packages/local-runtime/egress-admission/src/index.ts
|
|
22904
23017
|
var LOCAL_EGRESS_MODE_ENV2 = "ARCHCONTEXT_EGRESS_MODE";
|
|
22905
|
-
var LOCAL_EGRESS_CHANNELS2 = ["context7", "agent-audit", "github-issue-publishing", "npm-update-check", "codegraph-telemetry"];
|
|
23018
|
+
var LOCAL_EGRESS_CHANNELS2 = ["context7", "agent-audit", "github-issue-publishing", "npm-update-check", "projection-publishing", "codegraph-telemetry"];
|
|
22906
23019
|
|
|
22907
23020
|
class LocalEgressPolicyError2 extends Error {
|
|
22908
23021
|
}
|
|
@@ -22931,6 +23044,7 @@ function withLocalEgress2(channel, operation, env = process.env) {
|
|
|
22931
23044
|
}
|
|
22932
23045
|
|
|
22933
23046
|
// packages/local-runtime/codegraph-adapter/src/index.ts
|
|
23047
|
+
init_src();
|
|
22934
23048
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
22935
23049
|
import { createHash as createHash5 } from "node:crypto";
|
|
22936
23050
|
import { accessSync, closeSync as closeSync3, constants as fsConstants2, existsSync as existsSync6, openSync as openSync3, readFileSync as readFileSync7, readSync, realpathSync as realpathSync3, statSync as statSync3 } from "node:fs";
|
|
@@ -23932,10 +24046,15 @@ class CodeGraphCliProvider {
|
|
|
23932
24046
|
}
|
|
23933
24047
|
async indexAll(workspaceRoot) {
|
|
23934
24048
|
this.workspaceRoot = workspaceRoot;
|
|
23935
|
-
|
|
23936
|
-
throw new Error(`CodeGraph index missing at ${workspaceRoot}; run codegraph init before using archctxd`);
|
|
23937
|
-
}
|
|
24049
|
+
this.assertCanSync(workspaceRoot);
|
|
23938
24050
|
this.run(["sync", workspaceRoot]);
|
|
24051
|
+
this.assertReady(workspaceRoot);
|
|
24052
|
+
}
|
|
24053
|
+
assertReady(workspaceRoot) {
|
|
24054
|
+
assertRepositoryCodeFactsReady(workspaceRoot, this.binary === DEFAULT_CODEGRAPH_BINARY ? {} : { binary: this.binary });
|
|
24055
|
+
}
|
|
24056
|
+
assertCanSync(workspaceRoot) {
|
|
24057
|
+
this.assertReady(workspaceRoot);
|
|
23939
24058
|
}
|
|
23940
24059
|
async buildContext(task, options) {
|
|
23941
24060
|
const query = scopedTaskQuery(task, options.changedPaths ?? []);
|
|
@@ -23998,7 +24117,7 @@ class CodeGraphCliProvider {
|
|
|
23998
24117
|
}
|
|
23999
24118
|
run(args) {
|
|
24000
24119
|
disableCodeGraphTelemetryByDefault();
|
|
24001
|
-
const invocation = codeGraphCliInvocation(this.binary, this.workspaceRoot);
|
|
24120
|
+
const invocation = this.binary === DEFAULT_CODEGRAPH_BINARY ? packagedCodeGraphCliInvocation() : codeGraphCliInvocation(this.binary, this.workspaceRoot);
|
|
24002
24121
|
try {
|
|
24003
24122
|
return execFileSync2(invocation.command, [...invocation.argsPrefix, ...args], {
|
|
24004
24123
|
cwd: this.workspaceRoot,
|
|
@@ -24129,8 +24248,14 @@ class CodeGraphAdapter {
|
|
|
24129
24248
|
this.provider = provider;
|
|
24130
24249
|
disableCodeGraphTelemetryByDefault();
|
|
24131
24250
|
}
|
|
24251
|
+
assertCanSync(workspaceRoot) {
|
|
24252
|
+
this.assertCompatible();
|
|
24253
|
+
this.provider.assertCanSync?.(workspaceRoot);
|
|
24254
|
+
}
|
|
24132
24255
|
async ensureReady(workspace) {
|
|
24133
24256
|
this.assertCompatible();
|
|
24257
|
+
if (this.#snapshot)
|
|
24258
|
+
this.provider.assertReady?.(workspace.root);
|
|
24134
24259
|
this.#snapshot ??= await this.sync({ workspace });
|
|
24135
24260
|
return this.#snapshot;
|
|
24136
24261
|
}
|
|
@@ -24594,7 +24719,7 @@ function readProjectionHeadSha(root) {
|
|
|
24594
24719
|
function projectionCodeGraphStatusIssues(status, root, phase) {
|
|
24595
24720
|
const issue = (code, text) => ({
|
|
24596
24721
|
code,
|
|
24597
|
-
message: `CodeGraph ${phase} ${text}`
|
|
24722
|
+
message: phase === "repository" ? CODE_FACTS_ISSUE_MESSAGES[code] : `CodeGraph ${phase} ${text}`
|
|
24598
24723
|
});
|
|
24599
24724
|
if (!status.initialized)
|
|
24600
24725
|
return [issue("not-initialized", "status is not initialized")];
|
|
@@ -24703,7 +24828,7 @@ function inspectRepositoryCodeFacts(root, options = {}) {
|
|
|
24703
24828
|
message: (error instanceof Error ? error.message : String(error)).replace(/\s+/g, " ").trim().slice(0, 400)
|
|
24704
24829
|
};
|
|
24705
24830
|
}
|
|
24706
|
-
const versionIssues = codeGraphVersion === REQUIRED_CODEGRAPH_VERSION ? [] : [{ code: "version-mismatch", message:
|
|
24831
|
+
const versionIssues = codeGraphVersion === REQUIRED_CODEGRAPH_VERSION ? [] : [{ code: "version-mismatch", message: CODE_FACTS_ISSUE_MESSAGES["version-mismatch"] }];
|
|
24707
24832
|
if (!indexPresent) {
|
|
24708
24833
|
return { schemaVersion, state: "index-missing", indexPresent: false, requiredVersion: REQUIRED_CODEGRAPH_VERSION, codeGraphVersion, issues: versionIssues };
|
|
24709
24834
|
}
|
|
@@ -24733,12 +24858,25 @@ function inspectRepositoryCodeFacts(root, options = {}) {
|
|
|
24733
24858
|
current: status.index?.currentExtractionVersion ?? null
|
|
24734
24859
|
},
|
|
24735
24860
|
indexState: status.index?.state ?? null,
|
|
24736
|
-
reindexRecommended:
|
|
24861
|
+
reindexRecommended: issues.some((issue) => issue.code !== "pending-source-changes"),
|
|
24737
24862
|
pendingRefs: status.index?.pendingRefs ?? null,
|
|
24738
24863
|
pendingChanges: pending ? { added: pending.added, modified: pending.modified, removed: pending.removed } : null,
|
|
24739
24864
|
issues
|
|
24740
24865
|
};
|
|
24741
24866
|
}
|
|
24867
|
+
function assertRepositoryCodeFactsReady(root, options = {}) {
|
|
24868
|
+
const facts = inspectRepositoryCodeFacts(root, options);
|
|
24869
|
+
if (facts.state === "index-missing")
|
|
24870
|
+
throw new ProjectionCodeFactsUnavailableError("index-missing");
|
|
24871
|
+
if (facts.state === "unavailable")
|
|
24872
|
+
throw new FailureError("AC_CODE_FACTS_UNAVAILABLE", "code-facts-unavailable", {}, { action: "inspect-diagnostics", retryable: false });
|
|
24873
|
+
if (facts.state === "not-ready") {
|
|
24874
|
+
if (facts.issues.every((issue) => issue.code === "pending-source-changes"))
|
|
24875
|
+
return;
|
|
24876
|
+
const action = facts.codeGraphVersion !== REQUIRED_CODEGRAPH_VERSION ? "install-compatible-codegraph" : "codegraph-index";
|
|
24877
|
+
throw new FailureError("AC_CODE_FACTS_NOT_READY", "code-facts-not-ready", { issues: facts.issues }, { action });
|
|
24878
|
+
}
|
|
24879
|
+
}
|
|
24742
24880
|
function codeGraphIndexAvailable(workspaceRoot) {
|
|
24743
24881
|
return existsSync6(join3(workspaceRoot, ".codegraph"));
|
|
24744
24882
|
}
|
|
@@ -25061,6 +25199,7 @@ function parseExploreSymbols(output, maxSymbols) {
|
|
|
25061
25199
|
}
|
|
25062
25200
|
|
|
25063
25201
|
// packages/local-runtime/runtime-daemon/src/producer-failure.ts
|
|
25202
|
+
init_src();
|
|
25064
25203
|
function selectedProducerData(error, key) {
|
|
25065
25204
|
const descriptor = Object.getOwnPropertyDescriptor(error, key);
|
|
25066
25205
|
if (!descriptor || !("value" in descriptor))
|
|
@@ -26151,12 +26290,6 @@ function isPresent2(value) {
|
|
|
26151
26290
|
init_src();
|
|
26152
26291
|
import { existsSync as existsSync7, lstatSync as lstatSync5, mkdirSync as mkdirSync4, readdirSync as readdirSync4, readFileSync as readFileSync8, rmSync as rmSync4 } from "node:fs";
|
|
26153
26292
|
import { dirname as dirname5, resolve as resolve11 } from "node:path";
|
|
26154
|
-
|
|
26155
|
-
// packages/local-runtime/model-store-yaml/src/native-context.ts
|
|
26156
|
-
init_src();
|
|
26157
|
-
import { createHash as createHash6 } from "node:crypto";
|
|
26158
|
-
import { constants, closeSync as closeSync4, fstatSync as fstatSync2, lstatSync as lstatSync4, openSync as openSync4, readSync as readSync2, realpathSync as realpathSync4 } from "node:fs";
|
|
26159
|
-
import { join as join4, resolve as resolve10 } from "node:path";
|
|
26160
26293
|
// schemas/repo/architecture-node.schema.json
|
|
26161
26294
|
var architecture_node_schema_default = {
|
|
26162
26295
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
@@ -26245,6 +26378,12 @@ var architecture_node_schema_default = {
|
|
|
26245
26378
|
extensions: { type: "object", additionalProperties: true }
|
|
26246
26379
|
}
|
|
26247
26380
|
};
|
|
26381
|
+
|
|
26382
|
+
// packages/local-runtime/model-store-yaml/src/native-context.ts
|
|
26383
|
+
init_src();
|
|
26384
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
26385
|
+
import { constants, closeSync as closeSync4, fstatSync as fstatSync2, lstatSync as lstatSync4, openSync as openSync4, readSync as readSync2, realpathSync as realpathSync4 } from "node:fs";
|
|
26386
|
+
import { join as join4, resolve as resolve10 } from "node:path";
|
|
26248
26387
|
// schemas/repo/architecture-relation.schema.json
|
|
26249
26388
|
var architecture_relation_schema_default = {
|
|
26250
26389
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
@@ -26751,6 +26890,7 @@ function capture(source, snapshot) {
|
|
|
26751
26890
|
if (typeof budget !== "number" || !Number.isSafeInteger(budget) || budget <= 0)
|
|
26752
26891
|
throw new NativeContextError("native-context-budget-invalid", { path: MANIFEST, field: "runtime.contextBudgetBytes" });
|
|
26753
26892
|
const model = { nodes: [], relations: [], flows: [], sourcePaths: { nodes: {}, relations: {}, flows: {} } };
|
|
26893
|
+
const recordPaths = new Map;
|
|
26754
26894
|
for (const spec of specs)
|
|
26755
26895
|
if (spec.role !== "manifest") {
|
|
26756
26896
|
const value = record5(parseInput(source, spec.path));
|
|
@@ -26758,10 +26898,11 @@ function capture(source, snapshot) {
|
|
|
26758
26898
|
throw new NativeContextError("native-context-model-invalid", { path: spec.path, invariant: "entity-object-required" });
|
|
26759
26899
|
const kind = spec.role === "node" ? "nodes" : spec.role === "relation" ? "relations" : "flows";
|
|
26760
26900
|
model[kind].push(value);
|
|
26901
|
+
recordPaths.set(value, spec.path);
|
|
26761
26902
|
if (typeof value.id === "string")
|
|
26762
26903
|
model.sourcePaths[kind][value.id] = spec.path;
|
|
26763
26904
|
}
|
|
26764
|
-
const issues = nativeContextModelIssues(model, SCHEMAS);
|
|
26905
|
+
const issues = nativeContextModelIssues(model, SCHEMAS, recordPaths);
|
|
26765
26906
|
if (issues.length > 0)
|
|
26766
26907
|
throw new NativeContextError(issues.some((issue) => issue.invariant === "path-unsafe") ? "native-context-path-unsafe" : "native-context-model-invalid", { issues: issues.slice(0, 32), omittedDiagnosticCount: Math.max(0, issues.length - 32) });
|
|
26767
26908
|
for (const kind of MODEL_KINDS)
|
|
@@ -27100,6 +27241,7 @@ class YamlModelStore {
|
|
|
27100
27241
|
}
|
|
27101
27242
|
async validateModel(workspace) {
|
|
27102
27243
|
const errors = [];
|
|
27244
|
+
const nodeSchemaErrors = [];
|
|
27103
27245
|
for (const required of [".archcontext/manifest.yaml", ".archcontext/product.yaml"]) {
|
|
27104
27246
|
try {
|
|
27105
27247
|
readFileSync8(resolve11(workspace.root, required), "utf8");
|
|
@@ -27109,11 +27251,25 @@ class YamlModelStore {
|
|
|
27109
27251
|
}
|
|
27110
27252
|
const files = listModelFiles(workspace.root);
|
|
27111
27253
|
for (const file of files) {
|
|
27254
|
+
if (file.path.startsWith(".archcontext/model/") && /\.ya?ml$/.test(file.path)) {
|
|
27255
|
+
try {
|
|
27256
|
+
const value = parseJsonOrStableYaml(file.body, file.path);
|
|
27257
|
+
if (file.path.startsWith(".archcontext/model/nodes/")) {
|
|
27258
|
+
const { issues } = validateJsonSchema(architecture_node_schema_default, value);
|
|
27259
|
+
for (const issue of issues) {
|
|
27260
|
+
const error = `${file.path}: ${issue.path}: ${issue.message}`;
|
|
27261
|
+
errors.push(error);
|
|
27262
|
+
nodeSchemaErrors.push({ path: file.path, error });
|
|
27263
|
+
}
|
|
27264
|
+
continue;
|
|
27265
|
+
}
|
|
27266
|
+
} catch (error) {
|
|
27267
|
+
errors.push(`${file.path}: invalid model YAML: ${error instanceof Error ? error.message : String(error)}`);
|
|
27268
|
+
continue;
|
|
27269
|
+
}
|
|
27270
|
+
}
|
|
27112
27271
|
if (!file.schemaVersion.startsWith("archcontext."))
|
|
27113
27272
|
errors.push(`${file.path}: missing schemaVersion`);
|
|
27114
|
-
if (file.path.startsWith(".archcontext/model/nodes/") && file.schemaVersion !== "archcontext.node/v2") {
|
|
27115
|
-
errors.push(`${file.path}: expected archcontext.node/v2, got ${file.schemaVersion || "missing"}`);
|
|
27116
|
-
}
|
|
27117
27273
|
if (file.path.startsWith(".archcontext/model/flows/") && file.schemaVersion !== "archcontext.flow/v1") {
|
|
27118
27274
|
errors.push(`${file.path}: expected archcontext.flow/v1, got ${file.schemaVersion || "missing"}`);
|
|
27119
27275
|
}
|
|
@@ -27129,6 +27285,7 @@ class YamlModelStore {
|
|
|
27129
27285
|
valid: errors.length === 0,
|
|
27130
27286
|
errors,
|
|
27131
27287
|
...referenceErrors.length > 0 ? { referenceErrors } : {},
|
|
27288
|
+
...nodeSchemaErrors.length > 0 ? { nodeSchemaErrors } : {},
|
|
27132
27289
|
...warnings.length > 0 ? { warnings } : {},
|
|
27133
27290
|
modelDigest
|
|
27134
27291
|
};
|
|
@@ -33740,6 +33897,19 @@ async function runAgentContextProjectionCommand(input, cwd, daemon) {
|
|
|
33740
33897
|
throw publicOperationFailure(error);
|
|
33741
33898
|
}
|
|
33742
33899
|
}
|
|
33900
|
+
function agentContextProjectionDifferingPaths(root, paths) {
|
|
33901
|
+
const wanted = new Set(paths);
|
|
33902
|
+
if (wanted.size === 0)
|
|
33903
|
+
return [];
|
|
33904
|
+
const projection = buildAgentContextProjection(root);
|
|
33905
|
+
const onDisk = new Map(projection.existingFiles.map((file) => [file.path, file.onDisk]));
|
|
33906
|
+
const written = projectionFilesBoundToRenderInput(projection.existingFiles, projection.plan.files);
|
|
33907
|
+
const rendered = new Set(written.map((file) => file.path));
|
|
33908
|
+
return [...new Set([
|
|
33909
|
+
...written.filter((file) => wanted.has(file.path) && onDisk.get(file.path) !== file.body).map((file) => file.path),
|
|
33910
|
+
...[...wanted].filter((path) => !rendered.has(path))
|
|
33911
|
+
])].sort();
|
|
33912
|
+
}
|
|
33743
33913
|
function buildAgentContextProjection(root) {
|
|
33744
33914
|
const model = loadNativeModelFromArchContext(root);
|
|
33745
33915
|
const sourceDigest = digestJson({ model });
|
|
@@ -34042,7 +34212,7 @@ function readArchitectureDebtEvidence(root, input) {
|
|
|
34042
34212
|
capabilityByNodeId
|
|
34043
34213
|
} };
|
|
34044
34214
|
}
|
|
34045
|
-
function runArchitectureDebtCommand(cwd, input = {}) {
|
|
34215
|
+
function runArchitectureDebtCommand(cwd, input = {}, codeFactsOptions = {}) {
|
|
34046
34216
|
let root;
|
|
34047
34217
|
try {
|
|
34048
34218
|
root = findRepositoryRoot(cwd);
|
|
@@ -34050,6 +34220,8 @@ function runArchitectureDebtCommand(cwd, input = {}) {
|
|
|
34050
34220
|
return failureEnvelope("debt", knownFailure("AC_REPO_NOT_FOUND", "git-worktree-required", { checks: ["ownership", "anchors"] }));
|
|
34051
34221
|
}
|
|
34052
34222
|
try {
|
|
34223
|
+
if (codeGraphIndexAvailable(root))
|
|
34224
|
+
assertRepositoryCodeFactsReady(root, codeFactsOptions);
|
|
34053
34225
|
return okEnvelope("debt", readArchitectureDebt(root, input));
|
|
34054
34226
|
} catch (error) {
|
|
34055
34227
|
return selectedProducerFailureEnvelope("debt", error, "debt");
|
|
@@ -34424,6 +34596,8 @@ function effectiveEgressChannels(env, live) {
|
|
|
34424
34596
|
}
|
|
34425
34597
|
if (live.updateCheckRequested)
|
|
34426
34598
|
channels.push({ channel: "npm-update-check", destination: "configured npm registry", trigger: "explicit update --check or doctor --check-updates", data: "archctx package version lookup", status: "enabled" });
|
|
34599
|
+
if (live.projectionPublishRequested)
|
|
34600
|
+
channels.push({ channel: "projection-publishing", destination: "configured git remote, plus GitHub via gh or the operator's publisher command", trigger: "explicit archctx projection publish", data: "one commit of generated projection outputs and a pull request title/body listing their paths", status: "enabled" });
|
|
34427
34601
|
if (env[CODEGRAPH_TELEMETRY_ENV] !== undefined && env[CODEGRAPH_TELEMETRY_ENV] !== CODEGRAPH_TELEMETRY_DISABLED_VALUE)
|
|
34428
34602
|
channels.push({ channel: "codegraph-telemetry", destination: "CodeGraph configured telemetry endpoint", trigger: "CodeGraph execution with telemetry enabled", data: "third-party telemetry", status: "enabled" });
|
|
34429
34603
|
return channels.map((channel) => localEgressAdmission2(channel.channel, env).allowed ? channel : { ...channel, status: "blocked-by-policy" });
|
|
@@ -34458,9 +34632,9 @@ function localEgressStatus(env = process.env, live = {}) {
|
|
|
34458
34632
|
}
|
|
34459
34633
|
|
|
34460
34634
|
// packages/surfaces/cli/src/main.ts
|
|
34461
|
-
import { spawn as spawn2, spawnSync as
|
|
34462
|
-
import { accessSync as accessSync3, closeSync as
|
|
34463
|
-
import { dirname as dirname16, join as
|
|
34635
|
+
import { spawn as spawn2, spawnSync as spawnSync6 } from "child_process";
|
|
34636
|
+
import { accessSync as accessSync3, closeSync as closeSync13, constants as constants3, existsSync as existsSync35, mkdirSync as mkdirSync14, openSync as openSync13, readFileSync as readFileSync32, rmSync as rmSync16, statSync as statSync11 } from "fs";
|
|
34637
|
+
import { dirname as dirname16, join as join27, parse, resolve as resolve35 } from "path";
|
|
34464
34638
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
34465
34639
|
|
|
34466
34640
|
// packages/contracts/src/index.ts
|
|
@@ -34475,6 +34649,7 @@ init_ownership_dependencies();
|
|
|
34475
34649
|
init_ports();
|
|
34476
34650
|
init_projection();
|
|
34477
34651
|
init_projection_recovery();
|
|
34652
|
+
init_projection_writer();
|
|
34478
34653
|
init_projection_manifest();
|
|
34479
34654
|
init_refactor();
|
|
34480
34655
|
init_schema();
|
|
@@ -34596,10 +34771,10 @@ function assertAllowedArchContextPath2(root, relativePath, scope) {
|
|
|
34596
34771
|
}
|
|
34597
34772
|
|
|
34598
34773
|
// packages/core/architecture-domain/src/index.ts
|
|
34599
|
-
init_src();
|
|
34600
34774
|
import { createHash as createHash9 } from "node:crypto";
|
|
34601
34775
|
import { readFileSync as readFileSync14, realpathSync as realpathSync7, statSync as statSync4 } from "node:fs";
|
|
34602
34776
|
import { resolve as resolve18 } from "node:path";
|
|
34777
|
+
init_src();
|
|
34603
34778
|
var DEFAULT_IGNORES2 = new Set([
|
|
34604
34779
|
".git",
|
|
34605
34780
|
".codegraph",
|
|
@@ -34818,7 +34993,10 @@ var WINDOWS_CREATE_TIMEOUT_MS = 60000;
|
|
|
34818
34993
|
var WINDOWS_READ_TIMEOUT_MS = 1e4;
|
|
34819
34994
|
var REMOVE_HANDLE_RELEASE_MARGIN_MS = 2000;
|
|
34820
34995
|
var REMOVE_RETRY_DELAY_MS = 25;
|
|
34996
|
+
var WINDOWS_CONTROL_FILE_PHASES = ["script", "input", "parsed", "opened", "verified", "done"];
|
|
34997
|
+
var WINDOWS_CONTROL_FILE_PHASE_MARKER = /^archctx-acl-phase:([a-z]+)\r?$/;
|
|
34821
34998
|
var WINDOWS_CONTROL_FILE_SCRIPT = String.raw`
|
|
34999
|
+
[Console]::Error.WriteLine('archctx-acl-phase:script')
|
|
34822
35000
|
$ErrorActionPreference = 'Stop'
|
|
34823
35001
|
# A pwsh -> Node/Bun -> powershell.exe launch inherits incompatible PS7 module paths.
|
|
34824
35002
|
$env:PSModulePath = "$PSHOME\Modules"
|
|
@@ -34840,7 +35018,10 @@ function Test-PrivateAcl($stream, $sid) {
|
|
|
34840
35018
|
$rule.PropagationFlags -eq [System.Security.AccessControl.PropagationFlags]::None)
|
|
34841
35019
|
}
|
|
34842
35020
|
try {
|
|
34843
|
-
$
|
|
35021
|
+
$text = [Console]::In.ReadToEnd()
|
|
35022
|
+
[Console]::Error.WriteLine('archctx-acl-phase:input')
|
|
35023
|
+
$request = $text | ConvertFrom-Json
|
|
35024
|
+
[Console]::Error.WriteLine('archctx-acl-phase:parsed')
|
|
34844
35025
|
$sid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User
|
|
34845
35026
|
if ($request.operation -eq 'create') {
|
|
34846
35027
|
$acl = New-Object System.Security.AccessControl.FileSecurity
|
|
@@ -34850,18 +35031,24 @@ try {
|
|
|
34850
35031
|
$acl.AddAccessRule($rule)
|
|
34851
35032
|
$stream = New-Object System.IO.FileStream($request.path, [System.IO.FileMode]::CreateNew, [System.Security.AccessControl.FileSystemRights]::FullControl, [System.IO.FileShare]::None, 4096, [System.IO.FileOptions]::None, $acl)
|
|
34852
35033
|
$created = $true
|
|
35034
|
+
[Console]::Error.WriteLine('archctx-acl-phase:opened')
|
|
34853
35035
|
if (-not (Test-PrivateAcl $stream $sid)) { throw 'private ACL readback failed' }
|
|
35036
|
+
[Console]::Error.WriteLine('archctx-acl-phase:verified')
|
|
34854
35037
|
$bytes = [System.Text.Encoding]::UTF8.GetBytes([string]$request.body)
|
|
34855
35038
|
$stream.Write($bytes, 0, $bytes.Length)
|
|
34856
35039
|
$stream.Flush($true)
|
|
35040
|
+
[Console]::Error.WriteLine('archctx-acl-phase:done')
|
|
34857
35041
|
$result = @{ ok = $true }
|
|
34858
35042
|
} elseif ($request.operation -eq 'read') {
|
|
34859
35043
|
# Deny concurrent write/delete, validate the opened file, then read that same handle.
|
|
34860
35044
|
$stream = New-Object System.IO.FileStream($request.path, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::Read)
|
|
35045
|
+
[Console]::Error.WriteLine('archctx-acl-phase:opened')
|
|
34861
35046
|
if (-not (Test-PrivateAcl $stream $sid)) { throw 'private ACL readback failed' }
|
|
35047
|
+
[Console]::Error.WriteLine('archctx-acl-phase:verified')
|
|
34862
35048
|
if ($stream.Length -gt 1048576) { throw 'control file exceeds read budget' }
|
|
34863
35049
|
$reader = New-Object System.IO.StreamReader($stream, [System.Text.Encoding]::UTF8, $true, 4096, $true)
|
|
34864
35050
|
try { $body = $reader.ReadToEnd() } finally { $reader.Dispose() }
|
|
35051
|
+
[Console]::Error.WriteLine('archctx-acl-phase:done')
|
|
34865
35052
|
$result = @{ ok = $true; body = $body }
|
|
34866
35053
|
} else { throw 'unsupported operation' }
|
|
34867
35054
|
[Console]::Out.Write(($result | ConvertTo-Json -Compress))
|
|
@@ -34903,8 +35090,9 @@ function windowsControlFile(operation, path, body) {
|
|
|
34903
35090
|
const code = ["ETIMEDOUT", "ENOENT", "EACCES", "EPERM", "ENOBUFS"].includes(String(failure?.code)) ? String(failure?.code) : "UNKNOWN";
|
|
34904
35091
|
const signal = ["SIGTERM", "SIGKILL", "SIGABRT", "SIGINT"].includes(String(failure?.signal)) ? String(failure?.signal) : null;
|
|
34905
35092
|
const status = typeof failure?.status === "number" && Number.isInteger(failure.status) ? failure.status : null;
|
|
35093
|
+
const phase = lastWindowsControlFilePhase(failure?.stderr);
|
|
34906
35094
|
const elapsedMs = Math.round(performance.now() - startedAt);
|
|
34907
|
-
throw new Error(`Windows native ACL operation failed: ${JSON.stringify({ operation, code, status, signal, elapsedMs })}`);
|
|
35095
|
+
throw new Error(`Windows native ACL operation failed: ${JSON.stringify({ operation, code, status, signal, phase, elapsedMs })}`);
|
|
34908
35096
|
}
|
|
34909
35097
|
const result = JSON.parse(output);
|
|
34910
35098
|
if (!result || typeof result !== "object" || !("ok" in result))
|
|
@@ -34919,6 +35107,18 @@ function windowsControlFile(operation, path, body) {
|
|
|
34919
35107
|
return result.body;
|
|
34920
35108
|
}
|
|
34921
35109
|
}
|
|
35110
|
+
function lastWindowsControlFilePhase(stderr) {
|
|
35111
|
+
let phase = "none";
|
|
35112
|
+
if (typeof stderr !== "string")
|
|
35113
|
+
return phase;
|
|
35114
|
+
for (const line of stderr.split(`
|
|
35115
|
+
`)) {
|
|
35116
|
+
const name = WINDOWS_CONTROL_FILE_PHASE_MARKER.exec(line)?.[1];
|
|
35117
|
+
if (name && WINDOWS_CONTROL_FILE_PHASES.includes(name))
|
|
35118
|
+
phase = name;
|
|
35119
|
+
}
|
|
35120
|
+
return phase;
|
|
35121
|
+
}
|
|
34922
35122
|
function createPrivateControlFile(path, body) {
|
|
34923
35123
|
if (process.platform === "win32") {
|
|
34924
35124
|
windowsControlFile("create", path, body);
|
|
@@ -37988,7 +38188,7 @@ function readArchitectureDebtEvidence2(root, input) {
|
|
|
37988
38188
|
capabilityByNodeId
|
|
37989
38189
|
} };
|
|
37990
38190
|
}
|
|
37991
|
-
function runArchitectureDebtCommand2(cwd, input = {}) {
|
|
38191
|
+
function runArchitectureDebtCommand2(cwd, input = {}, codeFactsOptions = {}) {
|
|
37992
38192
|
let root;
|
|
37993
38193
|
try {
|
|
37994
38194
|
root = findRepositoryRoot(cwd);
|
|
@@ -37996,6 +38196,8 @@ function runArchitectureDebtCommand2(cwd, input = {}) {
|
|
|
37996
38196
|
return failureEnvelope("debt", knownFailure("AC_REPO_NOT_FOUND", "git-worktree-required", { checks: ["ownership", "anchors"] }));
|
|
37997
38197
|
}
|
|
37998
38198
|
try {
|
|
38199
|
+
if (codeGraphIndexAvailable(root))
|
|
38200
|
+
assertRepositoryCodeFactsReady(root, codeFactsOptions);
|
|
37999
38201
|
return okEnvelope("debt", readArchitectureDebt2(root, input));
|
|
38000
38202
|
} catch (error) {
|
|
38001
38203
|
return selectedProducerFailureEnvelope("debt", error, "debt");
|
|
@@ -42988,7 +43190,13 @@ class PracticeCheckpointService {
|
|
|
42988
43190
|
}
|
|
42989
43191
|
async prepareUnchecked(root, task, maxBytes = 12288, maxItems = 12, taskSessionId = "task_runtime") {
|
|
42990
43192
|
this.assertRunning();
|
|
43193
|
+
if (this.codeFacts.assertCanSync) {
|
|
43194
|
+
root = findRepositoryRoot(root);
|
|
43195
|
+
this.codeFacts.assertCanSync(root);
|
|
43196
|
+
}
|
|
42991
43197
|
const session = await this.openSession(root);
|
|
43198
|
+
if (this.codeFacts.assertCanSync)
|
|
43199
|
+
await this.codeFacts.ensureReady(session.workspace);
|
|
42992
43200
|
const result = await prepareTask({
|
|
42993
43201
|
workspace: session.workspace,
|
|
42994
43202
|
task,
|
|
@@ -54151,6 +54359,947 @@ function runtimeProjectionOwnedOutputDigest(projection) {
|
|
|
54151
54359
|
files: projection.files.map((file) => ({ path: file.path, body: file.body })).sort((left, right) => left.path.localeCompare(right.path))
|
|
54152
54360
|
});
|
|
54153
54361
|
}
|
|
54362
|
+
// packages/local-runtime/runtime-daemon/src/projection-writer.ts
|
|
54363
|
+
import { execFile as execFile2, spawnSync as spawnSync5 } from "node:child_process";
|
|
54364
|
+
import { createHash as createHash13, randomUUID as randomUUID3 } from "node:crypto";
|
|
54365
|
+
import { closeSync as closeSync9, existsSync as existsSync29, mkdirSync as mkdirSync12, mkdtempSync as mkdtempSync5, openSync as openSync10, readFileSync as readFileSync27, readSync as readSync5, realpathSync as realpathSync12, renameSync as renameSync7, rmSync as rmSync15, writeFileSync as writeFileSync11 } from "node:fs";
|
|
54366
|
+
import { tmpdir as tmpdir4 } from "node:os";
|
|
54367
|
+
import { extname as extname2, join as join23, resolve as resolve30 } from "node:path";
|
|
54368
|
+
init_src();
|
|
54369
|
+
var PROJECTION_PUBLISH_REQUEST_ID = "projection.publish";
|
|
54370
|
+
var PROJECTION_PUBLISH_STATUS_REQUEST_ID = "projection.publish-status";
|
|
54371
|
+
var PROJECTION_VERIFY_PR_REQUEST_ID = "projection.verify-pr";
|
|
54372
|
+
var PROJECTION_WRITER_DEFAULT_BASE = "main";
|
|
54373
|
+
var PROJECTION_WRITER_DEFAULT_REMOTE = "origin";
|
|
54374
|
+
var PROJECTION_PR_BODY_MARKER = "<!-- archctx-projection-writer -->";
|
|
54375
|
+
var PROJECTION_WRITER_STATE_SCHEMA_VERSION = "archcontext.projection-writer-state/v1";
|
|
54376
|
+
var PROJECTION_WRITER_MAX_ITERATIONS = 3;
|
|
54377
|
+
var PROJECTION_WRITER_TARGETS = ["agent-context", "architecture-docs"];
|
|
54378
|
+
var PUBLISHER_TIMEOUT_MS = 120000;
|
|
54379
|
+
var PR_BODY_MAX_LISTED_PATHS = 200;
|
|
54380
|
+
|
|
54381
|
+
class ProjectionWriterFailure extends Error {
|
|
54382
|
+
code;
|
|
54383
|
+
reasonCode;
|
|
54384
|
+
constructor(code, reasonCode, message) {
|
|
54385
|
+
super(message);
|
|
54386
|
+
this.code = code;
|
|
54387
|
+
this.reasonCode = reasonCode;
|
|
54388
|
+
this.name = "ProjectionWriterFailure";
|
|
54389
|
+
}
|
|
54390
|
+
}
|
|
54391
|
+
function projectionWriterPaths(repositoryRoot, remote, base, env = process.env) {
|
|
54392
|
+
const key = createHash13("sha256").update(`${remote}\x00${base}`, "utf8").digest("hex").slice(0, 16);
|
|
54393
|
+
const dir = join23(runtimeStatePaths(repositoryRoot, env).repositoryStateDir, "projection-writer", key);
|
|
54394
|
+
return { dir, worktree: join23(dir, "worktree"), state: join23(dir, "state.json"), lock: join23(dir, "lock") };
|
|
54395
|
+
}
|
|
54396
|
+
async function runProjectionPublishCommand(options) {
|
|
54397
|
+
const env = options.env ?? process.env;
|
|
54398
|
+
const now = options.now ?? (() => new Date);
|
|
54399
|
+
const remote = options.remote ?? PROJECTION_WRITER_DEFAULT_REMOTE;
|
|
54400
|
+
const base = options.base ?? PROJECTION_WRITER_DEFAULT_BASE;
|
|
54401
|
+
const branch = options.branch ?? PROJECTION_WRITER_DEFAULT_BRANCH;
|
|
54402
|
+
let repositoryRoot;
|
|
54403
|
+
try {
|
|
54404
|
+
repositoryRoot = findRepositoryRoot(options.cwd);
|
|
54405
|
+
} catch {
|
|
54406
|
+
return errorEnvelope(PROJECTION_PUBLISH_REQUEST_ID, "AC_REPO_NOT_FOUND", `projection publish requires a Git worktree at ${options.cwd}`, "git-worktree-required");
|
|
54407
|
+
}
|
|
54408
|
+
const invalid = projectionWriterTargetIssue(repositoryRoot, { remote, base, branch });
|
|
54409
|
+
if (invalid)
|
|
54410
|
+
return errorEnvelope(PROJECTION_PUBLISH_REQUEST_ID, "AC_SCHEMA_INVALID", invalid, "projection-writer-input-invalid");
|
|
54411
|
+
try {
|
|
54412
|
+
assertLocalEgressAllowed2("projection-publishing", env);
|
|
54413
|
+
} catch (error) {
|
|
54414
|
+
return errorEnvelope(PROJECTION_PUBLISH_REQUEST_ID, "AC_POLICY_VIOLATION", errorMessage2(error), "egress-policy-denied");
|
|
54415
|
+
}
|
|
54416
|
+
const startedAt = now().toISOString();
|
|
54417
|
+
const paths = projectionWriterPaths(repositoryRoot, remote, base, env);
|
|
54418
|
+
const blank = () => ({
|
|
54419
|
+
schemaVersion: PROJECTION_PUBLISH_RUN_SCHEMA_VERSION,
|
|
54420
|
+
result: "failed",
|
|
54421
|
+
remote,
|
|
54422
|
+
base,
|
|
54423
|
+
branch,
|
|
54424
|
+
baseSha: null,
|
|
54425
|
+
publishedCommit: null,
|
|
54426
|
+
pushed: false,
|
|
54427
|
+
pullRequest: null,
|
|
54428
|
+
publisher: options.publisher.kind,
|
|
54429
|
+
files: [],
|
|
54430
|
+
projectionStatus: null,
|
|
54431
|
+
humanActions: [],
|
|
54432
|
+
error: null,
|
|
54433
|
+
startedAt,
|
|
54434
|
+
finishedAt: startedAt,
|
|
54435
|
+
iterations: 0
|
|
54436
|
+
});
|
|
54437
|
+
let lock;
|
|
54438
|
+
try {
|
|
54439
|
+
mkdirSync12(paths.dir, { recursive: true, mode: 448 });
|
|
54440
|
+
lock = acquireProjectionWriterLock(paths.lock, startedAt);
|
|
54441
|
+
} catch (error) {
|
|
54442
|
+
const failure = asWriterFailure(error);
|
|
54443
|
+
return projectionPublishEnvelope({ ...blank(), finishedAt: now().toISOString(), error: writerError(failure) });
|
|
54444
|
+
}
|
|
54445
|
+
if (lock.kind === "busy") {
|
|
54446
|
+
return projectionPublishEnvelope({ ...blank(), result: "busy", holderPid: lock.pid, finishedAt: now().toISOString() });
|
|
54447
|
+
}
|
|
54448
|
+
try {
|
|
54449
|
+
let state;
|
|
54450
|
+
try {
|
|
54451
|
+
state = readProjectionWriterState(paths.state, remote, base);
|
|
54452
|
+
} catch (error) {
|
|
54453
|
+
return projectionPublishEnvelope({ ...blank(), finishedAt: now().toISOString(), error: writerError(asWriterFailure(error)) });
|
|
54454
|
+
}
|
|
54455
|
+
const outcome = await runLockedProjectionPublish({ options, env, now, repositoryRoot, paths, remote, base, branch, state, assertOwner: () => assertProjectionWriterOwner(paths.lock, lock.record), blank: blank() });
|
|
54456
|
+
const next = nextProjectionWriterState(state, outcome);
|
|
54457
|
+
try {
|
|
54458
|
+
writeProjectionWriterState(paths.state, next);
|
|
54459
|
+
} catch (error) {
|
|
54460
|
+
return projectionPublishEnvelope({
|
|
54461
|
+
...outcome.run,
|
|
54462
|
+
result: "failed",
|
|
54463
|
+
error: writerError(new ProjectionWriterFailure("AC_PRECONDITION_FAILED", "projection-writer-state-unwritable", errorMessage2(error)))
|
|
54464
|
+
});
|
|
54465
|
+
}
|
|
54466
|
+
return projectionPublishEnvelope(outcome.run);
|
|
54467
|
+
} finally {
|
|
54468
|
+
lock.release();
|
|
54469
|
+
}
|
|
54470
|
+
}
|
|
54471
|
+
async function runLockedProjectionPublish(input) {
|
|
54472
|
+
const { options, env, now, repositoryRoot, paths, remote, base, branch, state } = input;
|
|
54473
|
+
const runSequence = state.runSequence + 1;
|
|
54474
|
+
const publisherIdentity = options.publisher.identity ?? options.publisher.kind;
|
|
54475
|
+
let iterations = 0;
|
|
54476
|
+
let baseSha = null;
|
|
54477
|
+
const finish = (result, fields = {}, extra = {}) => ({
|
|
54478
|
+
run: { ...input.blank, ...fields, result, baseSha, iterations, finishedAt: now().toISOString() },
|
|
54479
|
+
runSequence,
|
|
54480
|
+
publisherIdentity,
|
|
54481
|
+
...extra
|
|
54482
|
+
});
|
|
54483
|
+
const failed = (failure, fields = {}, extra = {}) => finish("failed", { ...fields, error: writerError(failure) }, extra);
|
|
54484
|
+
try {
|
|
54485
|
+
const pushRemote = projectionWriterPushUrl(repositoryRoot, remote);
|
|
54486
|
+
while (iterations < PROJECTION_WRITER_MAX_ITERATIONS) {
|
|
54487
|
+
iterations += 1;
|
|
54488
|
+
remoteGit(repositoryRoot, ["fetch", "--no-tags", remote, `+refs/heads/${base}:refs/remotes/${remote}/${base}`], env);
|
|
54489
|
+
baseSha = git(repositoryRoot, ["rev-parse", "--verify", `refs/remotes/${remote}/${base}^{commit}`]).trim();
|
|
54490
|
+
const successMatches = state.lastSuccessfulBranch === branch && state.lastSuccessfulSha === baseSha;
|
|
54491
|
+
const pending = state.publisherPending?.branch === branch && state.publisherPending.baseSha === baseSha ? state.publisherPending : successMatches && state.lastSuccessfulCommit && state.lastSuccessfulPublisher !== publisherIdentity ? { branch, baseSha, commitSha: state.lastSuccessfulCommit, files: state.lastSuccessfulFiles } : null;
|
|
54492
|
+
if (!pending && successMatches && state.lastSuccessfulPublisher === publisherIdentity && (state.lastSuccessfulCommit === null || lsRemoteBranch(repositoryRoot, pushRemote, branch, env) === state.lastSuccessfulCommit)) {
|
|
54493
|
+
return finish("already-processed", { publishedCommit: state.lastSuccessfulCommit });
|
|
54494
|
+
}
|
|
54495
|
+
input.assertOwner();
|
|
54496
|
+
prepareProjectionWriterWorktree(repositoryRoot, paths.worktree, baseSha);
|
|
54497
|
+
if (pending && lsRemoteBranch(repositoryRoot, pushRemote, branch, env) === pending.commitSha) {
|
|
54498
|
+
input.assertOwner();
|
|
54499
|
+
const published = await publishPullRequest(options.publisher, { worktree: paths.worktree, remote, base, branch, baseSha, commitSha: pending.commitSha, files: pending.files });
|
|
54500
|
+
if (!published.ok) {
|
|
54501
|
+
return failed(published.failure, { publishedCommit: pending.commitSha, files: pending.files }, { publisherPending: pending });
|
|
54502
|
+
}
|
|
54503
|
+
return finish("published", { publishedCommit: pending.commitSha, files: pending.files, pullRequest: published.pullRequest });
|
|
54504
|
+
}
|
|
54505
|
+
const ownedPatterns = projectionOwnedPaths(loadNativeModelFromArchContext(paths.worktree));
|
|
54506
|
+
const snapshot = runProjectionSnapshotCommand(paths.worktree);
|
|
54507
|
+
if (!snapshot.ok)
|
|
54508
|
+
throw envelopeFailure(snapshot, "projection-writer-snapshot-failed");
|
|
54509
|
+
const expected = snapshot.data.expected;
|
|
54510
|
+
const request = {
|
|
54511
|
+
schemaVersion: PROJECTION_REQUEST_SCHEMA_VERSION,
|
|
54512
|
+
requestId: `projection_publish.${baseSha.slice(0, 12)}.${runSequence}-${iterations}`,
|
|
54513
|
+
profile: "repo-harness/v1",
|
|
54514
|
+
mode: "apply",
|
|
54515
|
+
targets: [...PROJECTION_WRITER_TARGETS],
|
|
54516
|
+
changedPaths: [],
|
|
54517
|
+
expected,
|
|
54518
|
+
...options.acceptObservedMajorChange ? { acceptObservedMajorChange: true } : {}
|
|
54519
|
+
};
|
|
54520
|
+
const applied = await options.apply(paths.worktree, request);
|
|
54521
|
+
input.assertOwner();
|
|
54522
|
+
if (!applied.ok) {
|
|
54523
|
+
const failure = envelopeFailure(applied, "projection-apply-failed");
|
|
54524
|
+
if (failure.code === "AC_CODE_FACTS_UNAVAILABLE") {
|
|
54525
|
+
throw new ProjectionWriterFailure(failure.code, failure.reasonCode, `${failure.message}; the writer never initializes CodeGraph itself: run \`codegraph init ${paths.worktree}\` once, then publish again`);
|
|
54526
|
+
}
|
|
54527
|
+
throw failure;
|
|
54528
|
+
}
|
|
54529
|
+
const result = applied.data;
|
|
54530
|
+
const projectionStatus = result.status;
|
|
54531
|
+
if (projectionStatus === "human-action-required" || projectionStatus === "adoption-required") {
|
|
54532
|
+
return finish("human-action-required", { projectionStatus, humanActions: publishHumanActions(result) });
|
|
54533
|
+
}
|
|
54534
|
+
if (!["noop", "applied", "applied-partial"].includes(projectionStatus)) {
|
|
54535
|
+
throw new ProjectionWriterFailure("AC_PRECONDITION_FAILED", "projection-apply-status-unsupported", `projection apply ended with status ${projectionStatus}; the writer publishes only noop, applied and applied-partial results`);
|
|
54536
|
+
}
|
|
54537
|
+
const changed = worktreeChangedPaths(paths.worktree);
|
|
54538
|
+
if (changed.length === 0)
|
|
54539
|
+
return finish("up-to-date", { projectionStatus });
|
|
54540
|
+
const written = new Set(result.files.filter((file) => file.action !== "unchanged").map((file) => file.path));
|
|
54541
|
+
const offending = changed.filter((path) => !written.has(path) || !isProjectionOwnedPath(path, ownedPatterns));
|
|
54542
|
+
if (offending.length > 0) {
|
|
54543
|
+
return failed(new ProjectionWriterFailure("AC_POLICY_VIOLATION", "projection-writer-non-owned-change", `projection apply changed paths the writer may not publish: ${offending.join(", ")}`), { projectionStatus });
|
|
54544
|
+
}
|
|
54545
|
+
input.assertOwner();
|
|
54546
|
+
git(paths.worktree, ["add", "-A", "--", ...changed]);
|
|
54547
|
+
const title = projectionPublishTitle(base, baseSha);
|
|
54548
|
+
try {
|
|
54549
|
+
git(paths.worktree, ["commit", "--no-verify", "-q", "-m", title, "-m", `Archctx-Projection-Source: ${baseSha}`]);
|
|
54550
|
+
} catch (error) {
|
|
54551
|
+
return failed(new ProjectionWriterFailure("AC_PRECONDITION_FAILED", "projection-writer-commit-failed", `git commit failed in the writer worktree; configure a git identity (user.name, user.email) for the repository or set GIT_AUTHOR_*/GIT_COMMITTER_*: ${errorMessage2(error)}`), { projectionStatus, files: changed });
|
|
54552
|
+
}
|
|
54553
|
+
const commitSha = git(paths.worktree, ["rev-parse", "HEAD"]).trim();
|
|
54554
|
+
const tip = lsRemoteBranch(repositoryRoot, remote, base, env);
|
|
54555
|
+
if (tip === null) {
|
|
54556
|
+
return failed(new ProjectionWriterFailure("AC_PRECONDITION_FAILED", "projection-writer-base-missing", `${remote} has no branch ${base}`), { projectionStatus, files: changed });
|
|
54557
|
+
}
|
|
54558
|
+
if (tip !== baseSha)
|
|
54559
|
+
continue;
|
|
54560
|
+
const lease = lsRemoteBranch(repositoryRoot, pushRemote, branch, env);
|
|
54561
|
+
let publishedCommit = commitSha;
|
|
54562
|
+
let pushed = false;
|
|
54563
|
+
if (lease !== null && carriesSameProjection(paths.worktree, lease, commitSha, baseSha)) {
|
|
54564
|
+
publishedCommit = lease;
|
|
54565
|
+
} else {
|
|
54566
|
+
input.assertOwner();
|
|
54567
|
+
remoteGit(paths.worktree, ["push", "--no-verify", `--force-with-lease=refs/heads/${branch}:${lease ?? ""}`, "--", pushRemote, `${commitSha}:refs/heads/${branch}`], env);
|
|
54568
|
+
pushed = true;
|
|
54569
|
+
}
|
|
54570
|
+
input.assertOwner();
|
|
54571
|
+
const published = await publishPullRequest(options.publisher, { worktree: paths.worktree, remote, base, branch, baseSha, commitSha: publishedCommit, files: changed });
|
|
54572
|
+
if (!published.ok) {
|
|
54573
|
+
const pendingPublish = { baseSha, branch, commitSha: publishedCommit, files: changed };
|
|
54574
|
+
return failed(published.failure, { projectionStatus, files: changed, publishedCommit, pushed }, { publisherPending: pendingPublish });
|
|
54575
|
+
}
|
|
54576
|
+
return finish("published", { projectionStatus, files: changed, publishedCommit, pushed, pullRequest: published.pullRequest });
|
|
54577
|
+
}
|
|
54578
|
+
return failed(new ProjectionWriterFailure("AC_PRECONDITION_FAILED", "projection-writer-base-unstable", `${remote}/${base} moved during each of ${PROJECTION_WRITER_MAX_ITERATIONS} attempts; nothing was pushed`));
|
|
54579
|
+
} catch (error) {
|
|
54580
|
+
return failed(asWriterFailure(error));
|
|
54581
|
+
}
|
|
54582
|
+
}
|
|
54583
|
+
function nextProjectionWriterState(state, outcome) {
|
|
54584
|
+
const run = outcome.run;
|
|
54585
|
+
const next = { ...state, branch: run.branch, runSequence: outcome.runSequence, lastRun: run };
|
|
54586
|
+
if (run.result === "already-processed")
|
|
54587
|
+
return next;
|
|
54588
|
+
if (run.baseSha !== null)
|
|
54589
|
+
next.lastProcessedSha = run.baseSha;
|
|
54590
|
+
if (run.result === "published" || run.result === "up-to-date") {
|
|
54591
|
+
next.lastSuccessfulSha = run.baseSha;
|
|
54592
|
+
next.lastSuccessfulBranch = run.branch;
|
|
54593
|
+
next.lastSuccessfulPublisher = outcome.publisherIdentity;
|
|
54594
|
+
next.lastSuccessfulFiles = run.files;
|
|
54595
|
+
next.lastSuccessfulCommit = run.result === "published" ? run.publishedCommit : null;
|
|
54596
|
+
next.publisherPending = null;
|
|
54597
|
+
} else if (outcome.publisherPending) {
|
|
54598
|
+
next.publisherPending = outcome.publisherPending;
|
|
54599
|
+
} else if (run.result === "human-action-required") {
|
|
54600
|
+
next.publisherPending = null;
|
|
54601
|
+
}
|
|
54602
|
+
return next;
|
|
54603
|
+
}
|
|
54604
|
+
function projectionPublishEnvelope(run) {
|
|
54605
|
+
if (run.result !== "failed")
|
|
54606
|
+
return okEnvelope(PROJECTION_PUBLISH_REQUEST_ID, run);
|
|
54607
|
+
const error = run.error ?? { code: "AC_UNCLASSIFIED_FAILURE", message: "projection publish failed" };
|
|
54608
|
+
return {
|
|
54609
|
+
...errorEnvelope(PROJECTION_PUBLISH_REQUEST_ID, error.code, error.message, error.reasonCode),
|
|
54610
|
+
data: run
|
|
54611
|
+
};
|
|
54612
|
+
}
|
|
54613
|
+
function publishHumanActions(result) {
|
|
54614
|
+
return result.humanActions.map((action) => ({ reasonCode: action.reasonCode, ...action.path ? { path: action.path } : {} }));
|
|
54615
|
+
}
|
|
54616
|
+
function projectionPublishTitle(base, baseSha) {
|
|
54617
|
+
return `chore(projection): refresh generated architecture outputs for ${base}@${baseSha.slice(0, 12)}`;
|
|
54618
|
+
}
|
|
54619
|
+
function projectionPublishBody(input) {
|
|
54620
|
+
const listed = input.files.slice(0, PR_BODY_MAX_LISTED_PATHS).map((path) => `- \`${path}\``);
|
|
54621
|
+
if (input.files.length > PR_BODY_MAX_LISTED_PATHS)
|
|
54622
|
+
listed.push(`- … and ${input.files.length - PR_BODY_MAX_LISTED_PATHS} more`);
|
|
54623
|
+
return [
|
|
54624
|
+
PROJECTION_PR_BODY_MARKER,
|
|
54625
|
+
"",
|
|
54626
|
+
`Base: \`${input.base}\` at \`${input.baseSha}\``,
|
|
54627
|
+
`Commit: \`${input.commitSha}\``,
|
|
54628
|
+
"",
|
|
54629
|
+
"Changed generated paths:",
|
|
54630
|
+
"",
|
|
54631
|
+
...listed,
|
|
54632
|
+
"",
|
|
54633
|
+
`Generated by \`archctx projection publish\`; touches generated paths only. Verify with \`archctx projection verify-pr --base ${input.base}\`.`,
|
|
54634
|
+
""
|
|
54635
|
+
].join(`
|
|
54636
|
+
`);
|
|
54637
|
+
}
|
|
54638
|
+
async function publishPullRequest(publisher, input) {
|
|
54639
|
+
if (publisher.kind === "none")
|
|
54640
|
+
return { ok: true, pullRequest: null };
|
|
54641
|
+
const dir = mkdtempSync5(join23(tmpdir4(), "archctx-projection-pr-body-"));
|
|
54642
|
+
try {
|
|
54643
|
+
const bodyFile = join23(dir, "body.md");
|
|
54644
|
+
writeFileSync11(bodyFile, projectionPublishBody(input), { mode: 384 });
|
|
54645
|
+
const pullRequest = await publisher.ensurePullRequest({
|
|
54646
|
+
worktree: input.worktree,
|
|
54647
|
+
remote: input.remote,
|
|
54648
|
+
base: input.base,
|
|
54649
|
+
branch: input.branch,
|
|
54650
|
+
baseSha: input.baseSha,
|
|
54651
|
+
commitSha: input.commitSha,
|
|
54652
|
+
title: projectionPublishTitle(input.base, input.baseSha),
|
|
54653
|
+
bodyFile
|
|
54654
|
+
});
|
|
54655
|
+
return { ok: true, pullRequest: { ...pullRequest.url ? { url: pullRequest.url } : {}, ...pullRequest.number !== undefined ? { number: pullRequest.number } : {} } };
|
|
54656
|
+
} catch (error) {
|
|
54657
|
+
if (error instanceof LocalEgressPolicyError2)
|
|
54658
|
+
return { ok: false, failure: new ProjectionWriterFailure("AC_POLICY_VIOLATION", "egress-policy-denied", error.message) };
|
|
54659
|
+
return { ok: false, failure: new ProjectionWriterFailure("AC_PRECONDITION_FAILED", "projection-publisher-failed", `${publisher.kind} publisher failed: ${errorMessage2(error)}`) };
|
|
54660
|
+
} finally {
|
|
54661
|
+
rmSync15(dir, { recursive: true, force: true });
|
|
54662
|
+
}
|
|
54663
|
+
}
|
|
54664
|
+
function runProjectionPublishStatusCommand(options) {
|
|
54665
|
+
const env = options.env ?? process.env;
|
|
54666
|
+
const remote = options.remote ?? PROJECTION_WRITER_DEFAULT_REMOTE;
|
|
54667
|
+
const base = options.base ?? PROJECTION_WRITER_DEFAULT_BASE;
|
|
54668
|
+
let repositoryRoot;
|
|
54669
|
+
try {
|
|
54670
|
+
repositoryRoot = findRepositoryRoot(options.cwd);
|
|
54671
|
+
} catch {
|
|
54672
|
+
return errorEnvelope(PROJECTION_PUBLISH_STATUS_REQUEST_ID, "AC_REPO_NOT_FOUND", `projection publish-status requires a Git worktree at ${options.cwd}`, "git-worktree-required");
|
|
54673
|
+
}
|
|
54674
|
+
const invalid = refNameIssue(repositoryRoot, "--base", base) ?? remoteNameIssue(remote);
|
|
54675
|
+
if (invalid)
|
|
54676
|
+
return errorEnvelope(PROJECTION_PUBLISH_STATUS_REQUEST_ID, "AC_SCHEMA_INVALID", invalid, "projection-writer-input-invalid");
|
|
54677
|
+
try {
|
|
54678
|
+
const paths = projectionWriterPaths(repositoryRoot, remote, base, env);
|
|
54679
|
+
const state = readProjectionWriterState(paths.state, remote, base);
|
|
54680
|
+
const holder = readProjectionWriterLock(paths.lock);
|
|
54681
|
+
const status = {
|
|
54682
|
+
schemaVersion: PROJECTION_PUBLISH_STATUS_SCHEMA_VERSION,
|
|
54683
|
+
remote,
|
|
54684
|
+
base,
|
|
54685
|
+
branch: state.branch,
|
|
54686
|
+
worktreePath: paths.worktree,
|
|
54687
|
+
running: holder && isProcessAlive(holder.pid) ? holder : null,
|
|
54688
|
+
lastProcessedSha: state.lastProcessedSha,
|
|
54689
|
+
lastSuccessfulSha: state.lastSuccessfulSha,
|
|
54690
|
+
lastRun: state.lastRun
|
|
54691
|
+
};
|
|
54692
|
+
return okEnvelope(PROJECTION_PUBLISH_STATUS_REQUEST_ID, status);
|
|
54693
|
+
} catch (error) {
|
|
54694
|
+
if (error instanceof ProjectionWriterFailure)
|
|
54695
|
+
return errorEnvelope(PROJECTION_PUBLISH_STATUS_REQUEST_ID, error.code, error.message, error.reasonCode);
|
|
54696
|
+
return publicOperationErrorEnvelope(PROJECTION_PUBLISH_STATUS_REQUEST_ID, error);
|
|
54697
|
+
}
|
|
54698
|
+
}
|
|
54699
|
+
async function runProjectionPublishFollow(options) {
|
|
54700
|
+
let runs = 0;
|
|
54701
|
+
while (!options.shouldStop()) {
|
|
54702
|
+
let envelope;
|
|
54703
|
+
try {
|
|
54704
|
+
envelope = await options.runOnce();
|
|
54705
|
+
} catch (error) {
|
|
54706
|
+
envelope = publicOperationErrorEnvelope(PROJECTION_PUBLISH_REQUEST_ID, error);
|
|
54707
|
+
}
|
|
54708
|
+
runs += 1;
|
|
54709
|
+
options.onResult(envelope);
|
|
54710
|
+
if (options.shouldStop())
|
|
54711
|
+
break;
|
|
54712
|
+
await options.sleep(options.intervalMs);
|
|
54713
|
+
}
|
|
54714
|
+
return runs;
|
|
54715
|
+
}
|
|
54716
|
+
function runProjectionVerifyPrCommand(options) {
|
|
54717
|
+
const requestId = PROJECTION_VERIFY_PR_REQUEST_ID;
|
|
54718
|
+
let root;
|
|
54719
|
+
try {
|
|
54720
|
+
root = findRepositoryRoot(options.cwd);
|
|
54721
|
+
} catch {
|
|
54722
|
+
return errorEnvelope(requestId, "AC_REPO_NOT_FOUND", `projection verify-pr requires a Git worktree at ${options.cwd}`, "git-worktree-required");
|
|
54723
|
+
}
|
|
54724
|
+
if (!isGitRevisionArgument(options.base))
|
|
54725
|
+
return errorEnvelope(requestId, "AC_SCHEMA_INVALID", `projection verify-pr --base is not a usable revision: ${JSON.stringify(options.base)}`, "projection-verify-pr-input-invalid");
|
|
54726
|
+
try {
|
|
54727
|
+
if (!isTrackedWorktreeClean(root)) {
|
|
54728
|
+
return errorEnvelope(requestId, "AC_PRECONDITION_FAILED", "projection verify-pr requires a clean tracked worktree; HEAD is the pull request head", "projection-verify-pr-worktree-dirty");
|
|
54729
|
+
}
|
|
54730
|
+
let baseSha;
|
|
54731
|
+
let mergeBase;
|
|
54732
|
+
const headSha = git(root, ["rev-parse", "--verify", "HEAD^{commit}"]).trim();
|
|
54733
|
+
try {
|
|
54734
|
+
baseSha = git(root, ["rev-parse", "--verify", `${options.base}^{commit}`]).trim();
|
|
54735
|
+
mergeBase = git(root, ["merge-base", baseSha, headSha]).trim();
|
|
54736
|
+
} catch (error) {
|
|
54737
|
+
return errorEnvelope(requestId, "AC_PRECONDITION_FAILED", `projection verify-pr cannot resolve ${options.base} against HEAD: ${errorMessage2(error)}`, "projection-verify-pr-base-unresolved");
|
|
54738
|
+
}
|
|
54739
|
+
const changedPaths = [...new Set(git(root, ["diff", "--name-only", "-z", "--no-renames", `${mergeBase}..${headSha}`, "--"]).split("\x00").filter(Boolean))].sort();
|
|
54740
|
+
const verification = (fields) => okEnvelope(requestId, {
|
|
54741
|
+
schemaVersion: PROJECTION_PR_VERIFICATION_SCHEMA_VERSION,
|
|
54742
|
+
base: options.base,
|
|
54743
|
+
baseSha,
|
|
54744
|
+
headSha,
|
|
54745
|
+
mergeBase,
|
|
54746
|
+
changedPaths,
|
|
54747
|
+
nonGeneratedPaths: [],
|
|
54748
|
+
humanEditedPaths: [],
|
|
54749
|
+
generatedOnly: false,
|
|
54750
|
+
renderEquivalent: null,
|
|
54751
|
+
differingPaths: [],
|
|
54752
|
+
projectionStatus: null,
|
|
54753
|
+
...fields
|
|
54754
|
+
});
|
|
54755
|
+
if (changedPaths.length === 0)
|
|
54756
|
+
return verification({ verdict: "no-changes" });
|
|
54757
|
+
const classified = classifyPullRequestPaths(root, mergeBase, headSha, changedPaths);
|
|
54758
|
+
if (classified.nonGeneratedPaths.length > 0) {
|
|
54759
|
+
return verification({ verdict: "touches-non-generated", nonGeneratedPaths: classified.nonGeneratedPaths, humanEditedPaths: classified.humanEditedPaths });
|
|
54760
|
+
}
|
|
54761
|
+
const snapshot = runProjectionSnapshotCommand(root);
|
|
54762
|
+
if (!snapshot.ok)
|
|
54763
|
+
return { ...snapshot, requestId };
|
|
54764
|
+
const request = {
|
|
54765
|
+
schemaVersion: PROJECTION_REQUEST_SCHEMA_VERSION,
|
|
54766
|
+
requestId: `projection_verify_pr.${headSha.slice(0, 12)}`,
|
|
54767
|
+
profile: "repo-harness/v1",
|
|
54768
|
+
mode: "check",
|
|
54769
|
+
targets: [...PROJECTION_WRITER_TARGETS],
|
|
54770
|
+
changedPaths: [],
|
|
54771
|
+
expected: snapshot.data.expected
|
|
54772
|
+
};
|
|
54773
|
+
const checked = (options.check ?? runProjectionCheckInvocation)(request, root);
|
|
54774
|
+
if (!checked.ok)
|
|
54775
|
+
return { ...checked, requestId };
|
|
54776
|
+
const result = checked.data;
|
|
54777
|
+
let agentContextDiffering;
|
|
54778
|
+
try {
|
|
54779
|
+
agentContextDiffering = agentContextProjectionDifferingPaths(root, classified.agentContextPaths);
|
|
54780
|
+
} catch {
|
|
54781
|
+
agentContextDiffering = [...classified.agentContextPaths];
|
|
54782
|
+
}
|
|
54783
|
+
const differingPaths = [...new Set([
|
|
54784
|
+
...result.files.filter((file) => file.action !== "unchanged").map((file) => file.path),
|
|
54785
|
+
...agentContextDiffering
|
|
54786
|
+
])].sort();
|
|
54787
|
+
const renderEquivalent = result.status === "noop" && agentContextDiffering.length === 0;
|
|
54788
|
+
return verification({
|
|
54789
|
+
verdict: renderEquivalent ? "generated-only-render-equivalent" : "generated-only-not-render-equivalent",
|
|
54790
|
+
generatedOnly: true,
|
|
54791
|
+
renderEquivalent,
|
|
54792
|
+
differingPaths,
|
|
54793
|
+
projectionStatus: result.status
|
|
54794
|
+
});
|
|
54795
|
+
} catch (error) {
|
|
54796
|
+
return publicOperationErrorEnvelope(requestId, error);
|
|
54797
|
+
}
|
|
54798
|
+
}
|
|
54799
|
+
function classifyPullRequestPaths(root, mergeBase, headSha, changedPaths) {
|
|
54800
|
+
const baseTargets = projectionTargetsAtRevision(root, mergeBase);
|
|
54801
|
+
const headTargets = projectionTargetsAtRevision(root, headSha);
|
|
54802
|
+
const nonGenerated = [];
|
|
54803
|
+
const humanEdited = [];
|
|
54804
|
+
const agentContext = [];
|
|
54805
|
+
for (const path of changedPaths) {
|
|
54806
|
+
if (path === ARCHITECTURE_DOCS_PROJECTION_MANIFEST_PATH)
|
|
54807
|
+
continue;
|
|
54808
|
+
const atBase = baseTargets.get(path);
|
|
54809
|
+
const atHead = headTargets.get(path);
|
|
54810
|
+
if (!atBase && !atHead) {
|
|
54811
|
+
nonGenerated.push(path);
|
|
54812
|
+
continue;
|
|
54813
|
+
}
|
|
54814
|
+
const before = readFileAtRevision(root, mergeBase, path);
|
|
54815
|
+
const after = readFileAtRevision(root, headSha, path);
|
|
54816
|
+
if (!atHead && after !== null) {
|
|
54817
|
+
nonGenerated.push(path);
|
|
54818
|
+
continue;
|
|
54819
|
+
}
|
|
54820
|
+
const target = {
|
|
54821
|
+
whole: (atBase?.whole ?? false) || (atHead?.whole ?? false),
|
|
54822
|
+
mixedDocument: (atBase?.mixedDocument ?? false) || (atHead?.mixedDocument ?? false),
|
|
54823
|
+
agentNodeIds: [...new Set([...atBase?.agentNodeIds ?? [], ...atHead?.agentNodeIds ?? []])].sort()
|
|
54824
|
+
};
|
|
54825
|
+
if (!target.mixedDocument && target.agentNodeIds.length === 0)
|
|
54826
|
+
continue;
|
|
54827
|
+
if (!mixedTargetTextUnchanged(before, after, target)) {
|
|
54828
|
+
humanEdited.push(path);
|
|
54829
|
+
nonGenerated.push(path);
|
|
54830
|
+
} else if (target.agentNodeIds.length > 0) {
|
|
54831
|
+
agentContext.push(path);
|
|
54832
|
+
}
|
|
54833
|
+
}
|
|
54834
|
+
return { nonGeneratedPaths: nonGenerated.sort(), humanEditedPaths: humanEdited.sort(), agentContextPaths: agentContext.sort() };
|
|
54835
|
+
}
|
|
54836
|
+
function projectionTargetsAtRevision(root, revision) {
|
|
54837
|
+
const targets = new Map;
|
|
54838
|
+
const add = (path, ownership) => {
|
|
54839
|
+
const known = targets.get(path) ?? { whole: false, mixedDocument: false, agentNodeIds: [] };
|
|
54840
|
+
targets.set(path, {
|
|
54841
|
+
whole: known.whole || ownership.whole === true,
|
|
54842
|
+
mixedDocument: known.mixedDocument || ownership.mixedDocument === true,
|
|
54843
|
+
agentNodeIds: [...known.agentNodeIds, ...ownership.agentNodeIds ?? []]
|
|
54844
|
+
});
|
|
54845
|
+
};
|
|
54846
|
+
try {
|
|
54847
|
+
const manifest = readFileAtRevision(root, revision, ARCHITECTURE_DOCS_PROJECTION_MANIFEST_PATH);
|
|
54848
|
+
const rows = manifest === null ? {} : JSON.parse(manifest).targets;
|
|
54849
|
+
if (rows && typeof rows === "object" && !Array.isArray(rows)) {
|
|
54850
|
+
for (const row of Object.values(rows)) {
|
|
54851
|
+
if (!row || typeof row.path !== "string")
|
|
54852
|
+
continue;
|
|
54853
|
+
add(row.path, row.ownership === "generated" ? { whole: true } : { mixedDocument: true });
|
|
54854
|
+
}
|
|
54855
|
+
}
|
|
54856
|
+
} catch {}
|
|
54857
|
+
try {
|
|
54858
|
+
const listed = git(root, ["ls-tree", "-r", "-z", "--name-only", revision, "--", ".archcontext/model"]).split("\x00").filter(Boolean);
|
|
54859
|
+
const files = new Map(listed.map((path) => [path, readFileAtRevision(root, revision, path) ?? ""]));
|
|
54860
|
+
for (const target of agentContextProjectionTargetPaths(loadNativeModelFromModelFiles(files)))
|
|
54861
|
+
add(target.path, { agentNodeIds: [target.nodeId] });
|
|
54862
|
+
} catch {}
|
|
54863
|
+
return targets;
|
|
54864
|
+
}
|
|
54865
|
+
function mixedTargetTextUnchanged(before, after, target) {
|
|
54866
|
+
if (before === null || after === null)
|
|
54867
|
+
return false;
|
|
54868
|
+
try {
|
|
54869
|
+
return JSON.stringify(textOutsideGeneratedRegions(before, target)) === JSON.stringify(textOutsideGeneratedRegions(after, target));
|
|
54870
|
+
} catch {
|
|
54871
|
+
return false;
|
|
54872
|
+
}
|
|
54873
|
+
}
|
|
54874
|
+
function textOutsideGeneratedRegions(body, target) {
|
|
54875
|
+
const text = target.agentNodeIds.length > 0 ? agentContextTextOutsideRegions(body, target.agentNodeIds) : body;
|
|
54876
|
+
if (!target.mixedDocument)
|
|
54877
|
+
return [text];
|
|
54878
|
+
const scan = scanDocumentRegions(text);
|
|
54879
|
+
if (scan.diagnostics.length > 0 || scan.generatedRegions.some((region) => !region.closed))
|
|
54880
|
+
throw new Error("projection-generated-regions-unbalanced");
|
|
54881
|
+
return scan.humanRegions.map((region) => region.text);
|
|
54882
|
+
}
|
|
54883
|
+
function readFileAtRevision(root, revision, path) {
|
|
54884
|
+
if (!gitSucceeds2(root, ["cat-file", "-e", `${revision}:${path}`]))
|
|
54885
|
+
return null;
|
|
54886
|
+
return git(root, ["cat-file", "blob", `${revision}:${path}`]);
|
|
54887
|
+
}
|
|
54888
|
+
function createNoneProjectionPublisher() {
|
|
54889
|
+
return { kind: "none", ensurePullRequest: async () => ({}) };
|
|
54890
|
+
}
|
|
54891
|
+
function createGhProjectionPublisher(options = {}) {
|
|
54892
|
+
const exec = options.exec ?? nodeProjectionPublisherExec;
|
|
54893
|
+
const admissionEnv = options.env ?? process.env;
|
|
54894
|
+
const timeoutMs = options.timeoutMs ?? PUBLISHER_TIMEOUT_MS;
|
|
54895
|
+
return {
|
|
54896
|
+
kind: "gh",
|
|
54897
|
+
async ensurePullRequest(input) {
|
|
54898
|
+
assertLocalEgressAllowed2("projection-publishing", admissionEnv);
|
|
54899
|
+
const repository = projectionPublisherRepository(input.worktree, input.remote);
|
|
54900
|
+
const env = { ...stringEnv(process.env), GH_PROMPT_DISABLED: "1" };
|
|
54901
|
+
delete env.GH_REPO;
|
|
54902
|
+
const gh = (args) => withLocalEgress2("projection-publishing", () => exec("gh", [...args, "--repo", repository], { cwd: input.worktree, env, timeoutMs }), admissionEnv);
|
|
54903
|
+
const listed = await gh(["pr", "list", "--head", input.branch, "--base", input.base, "--state", "open", "--json", "number,url", "--limit", "1"]);
|
|
54904
|
+
const open = parseGhPullRequestList(listed.stdout);
|
|
54905
|
+
if (open) {
|
|
54906
|
+
await gh(["pr", "edit", String(open.number), "--title", input.title, "--body-file", input.bodyFile]);
|
|
54907
|
+
return { number: open.number, ...open.url ? { url: open.url } : {} };
|
|
54908
|
+
}
|
|
54909
|
+
const created = await gh(["pr", "create", "--base", input.base, "--head", input.branch, "--title", input.title, "--body-file", input.bodyFile]);
|
|
54910
|
+
const url = created.stdout.trim().split(`
|
|
54911
|
+
`).map((line) => line.trim()).filter(Boolean).pop() ?? "";
|
|
54912
|
+
const number = /\/pull\/(\d+)(?:[/?#]|$)/.exec(url)?.[1];
|
|
54913
|
+
return { ...url ? { url } : {}, ...number ? { number: Number(number) } : {} };
|
|
54914
|
+
}
|
|
54915
|
+
};
|
|
54916
|
+
}
|
|
54917
|
+
function projectionWriterPushUrl(worktree, remote) {
|
|
54918
|
+
const urls = git(worktree, ["remote", "get-url", "--push", "--all", "--", remote]).trim().split(`
|
|
54919
|
+
`).filter(Boolean);
|
|
54920
|
+
if (urls.length !== 1) {
|
|
54921
|
+
throw new ProjectionWriterFailure("AC_SCHEMA_INVALID", "projection-writer-push-target-ambiguous", "projection publish requires exactly one push URL for the selected remote");
|
|
54922
|
+
}
|
|
54923
|
+
return urls[0];
|
|
54924
|
+
}
|
|
54925
|
+
function projectionPublisherRepository(worktree, remote) {
|
|
54926
|
+
const source = projectionWriterPushUrl(worktree, remote);
|
|
54927
|
+
const scp = /^[^/@:]+@([A-Za-z0-9.-]+):([^?#]+)$/.exec(source);
|
|
54928
|
+
let host;
|
|
54929
|
+
let path;
|
|
54930
|
+
if (scp) {
|
|
54931
|
+
host = scp[1];
|
|
54932
|
+
path = scp[2];
|
|
54933
|
+
} else {
|
|
54934
|
+
const url = new URL(source);
|
|
54935
|
+
if (!["https:", "ssh:"].includes(url.protocol) || url.search || url.hash || url.password || url.protocol === "https:" && url.username) {
|
|
54936
|
+
throw new Error("the selected remote must be an SSH or HTTPS GitHub repository URL");
|
|
54937
|
+
}
|
|
54938
|
+
host = url.protocol === "ssh:" ? url.hostname : url.host;
|
|
54939
|
+
path = url.pathname.replace(/^\//, "");
|
|
54940
|
+
}
|
|
54941
|
+
path = path.replace(/\.git$/, "");
|
|
54942
|
+
if (!/^[A-Za-z0-9.-]+(?::[0-9]+)?$/.test(host) || !/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(path) || path.split("/").some((part) => part === "." || part === "..")) {
|
|
54943
|
+
throw new Error("the selected remote must identify one GitHub host, owner, and repository");
|
|
54944
|
+
}
|
|
54945
|
+
return `${host.toLowerCase()}/${path}`;
|
|
54946
|
+
}
|
|
54947
|
+
function createCommandProjectionPublisher(options) {
|
|
54948
|
+
const exec = options.exec ?? nodeProjectionPublisherExec;
|
|
54949
|
+
const admissionEnv = options.env ?? process.env;
|
|
54950
|
+
const sourceEnv = options.sourceEnv ?? process.env;
|
|
54951
|
+
const platform = options.platform ?? process.platform;
|
|
54952
|
+
const timeoutMs = options.timeoutMs ?? PUBLISHER_TIMEOUT_MS;
|
|
54953
|
+
return {
|
|
54954
|
+
kind: "command",
|
|
54955
|
+
identity: `command:${resolve30(options.command)}`,
|
|
54956
|
+
async ensurePullRequest(input) {
|
|
54957
|
+
const env = {
|
|
54958
|
+
...commandPublisherBaseEnv(sourceEnv, platform),
|
|
54959
|
+
ARCHCTX_PUBLISH_REMOTE: input.remote,
|
|
54960
|
+
ARCHCTX_PUBLISH_BASE: input.base,
|
|
54961
|
+
ARCHCTX_PUBLISH_BRANCH: input.branch,
|
|
54962
|
+
ARCHCTX_PUBLISH_BASE_SHA: input.baseSha,
|
|
54963
|
+
ARCHCTX_PUBLISH_COMMIT: input.commitSha,
|
|
54964
|
+
ARCHCTX_PUBLISH_TITLE: input.title,
|
|
54965
|
+
ARCHCTX_PUBLISH_BODY_FILE: input.bodyFile
|
|
54966
|
+
};
|
|
54967
|
+
const { stdout } = await withLocalEgress2("projection-publishing", async () => {
|
|
54968
|
+
const invocation = commandPublisherInvocation(options.command, platform);
|
|
54969
|
+
try {
|
|
54970
|
+
return await exec(invocation.file, invocation.args, { cwd: input.worktree, env, timeoutMs });
|
|
54971
|
+
} catch (error) {
|
|
54972
|
+
if (invocation.interpreter !== undefined && error.code === "ENOENT") {
|
|
54973
|
+
throw new Error(`publisher command ${options.command} needs the interpreter "${invocation.interpreter}" from its #! line, which was not found on PATH`);
|
|
54974
|
+
}
|
|
54975
|
+
throw error;
|
|
54976
|
+
}
|
|
54977
|
+
}, admissionEnv);
|
|
54978
|
+
return parsePublisherCommandOutput(stdout);
|
|
54979
|
+
}
|
|
54980
|
+
};
|
|
54981
|
+
}
|
|
54982
|
+
var COMMAND_PUBLISHER_POSIX_ENV = ["PATH", "HOME"];
|
|
54983
|
+
var COMMAND_PUBLISHER_WINDOWS_ENV = ["PATH", "HOME", "SystemRoot", "windir", "ComSpec", "PATHEXT", "TEMP", "TMP", "USERPROFILE"];
|
|
54984
|
+
function commandPublisherBaseEnv(source, platform) {
|
|
54985
|
+
const env = {};
|
|
54986
|
+
if (platform !== "win32") {
|
|
54987
|
+
for (const name of COMMAND_PUBLISHER_POSIX_ENV) {
|
|
54988
|
+
const value = source[name];
|
|
54989
|
+
if (value)
|
|
54990
|
+
env[name] = value;
|
|
54991
|
+
}
|
|
54992
|
+
return env;
|
|
54993
|
+
}
|
|
54994
|
+
const allowed = new Set(COMMAND_PUBLISHER_WINDOWS_ENV.map((name) => name.toUpperCase()));
|
|
54995
|
+
const forwarded = new Set;
|
|
54996
|
+
for (const [name, value] of Object.entries(source)) {
|
|
54997
|
+
const key = name.toUpperCase();
|
|
54998
|
+
if (!value || !allowed.has(key) || forwarded.has(key))
|
|
54999
|
+
continue;
|
|
55000
|
+
forwarded.add(key);
|
|
55001
|
+
env[name] = value;
|
|
55002
|
+
}
|
|
55003
|
+
return env;
|
|
55004
|
+
}
|
|
55005
|
+
function commandPublisherInvocation(command, platform) {
|
|
55006
|
+
if (platform !== "win32")
|
|
55007
|
+
return { file: command, args: [] };
|
|
55008
|
+
const extension = extname2(command).toLowerCase();
|
|
55009
|
+
if (extension === ".cmd" || extension === ".bat") {
|
|
55010
|
+
throw new Error(`publisher command ${command} is a ${extension} script, which only runs through cmd.exe; the command publisher runs without a shell, so use an executable or a #! script`);
|
|
55011
|
+
}
|
|
55012
|
+
const firstLine = readCommandFirstLine(command);
|
|
55013
|
+
if (!firstLine.startsWith("#!"))
|
|
55014
|
+
return { file: command, args: [] };
|
|
55015
|
+
const tokens = firstLine.slice(2).trim().split(/\s+/).filter((token) => token !== "");
|
|
55016
|
+
if (tokens.length === 0)
|
|
55017
|
+
throw new Error(`publisher command ${command} has an empty #! line`);
|
|
55018
|
+
let interpreter = commandBasename(tokens[0]);
|
|
55019
|
+
let interpreterArgs = tokens.slice(1);
|
|
55020
|
+
if (interpreter === "env") {
|
|
55021
|
+
const next = interpreterArgs[0];
|
|
55022
|
+
if (next === undefined || next.startsWith("-")) {
|
|
55023
|
+
throw new Error(`publisher command ${command} has an unsupported #! line "${firstLine}": /usr/bin/env must be followed by an interpreter name`);
|
|
55024
|
+
}
|
|
55025
|
+
interpreter = commandBasename(next);
|
|
55026
|
+
interpreterArgs = interpreterArgs.slice(1);
|
|
55027
|
+
}
|
|
55028
|
+
return { file: interpreter, args: [...interpreterArgs, command], interpreter };
|
|
55029
|
+
}
|
|
55030
|
+
function commandBasename(token) {
|
|
55031
|
+
const name = token.split(/[\\/]/).pop() ?? token;
|
|
55032
|
+
return name.toLowerCase().endsWith(".exe") ? name.slice(0, -4) : name;
|
|
55033
|
+
}
|
|
55034
|
+
function readCommandFirstLine(command) {
|
|
55035
|
+
let fd;
|
|
55036
|
+
try {
|
|
55037
|
+
fd = openSync10(command, "r");
|
|
55038
|
+
} catch (error) {
|
|
55039
|
+
throw new Error(`publisher command ${command} cannot be read: ${error instanceof Error ? error.message : String(error)}`);
|
|
55040
|
+
}
|
|
55041
|
+
try {
|
|
55042
|
+
const buffer = Buffer.alloc(1024);
|
|
55043
|
+
const length = readSync5(fd, buffer, 0, buffer.length, 0);
|
|
55044
|
+
const head = buffer.subarray(0, length).toString("utf8");
|
|
55045
|
+
const newline = head.indexOf(`
|
|
55046
|
+
`);
|
|
55047
|
+
return (newline === -1 ? head : head.slice(0, newline)).replace(/\r$/, "");
|
|
55048
|
+
} finally {
|
|
55049
|
+
closeSync9(fd);
|
|
55050
|
+
}
|
|
55051
|
+
}
|
|
55052
|
+
function parseGhPullRequestList(stdout) {
|
|
55053
|
+
const parsed = JSON.parse(stdout.trim() === "" ? "[]" : stdout);
|
|
55054
|
+
if (!Array.isArray(parsed))
|
|
55055
|
+
throw new Error("gh pr list returned an unexpected shape");
|
|
55056
|
+
const first = parsed[0];
|
|
55057
|
+
if (!first)
|
|
55058
|
+
return;
|
|
55059
|
+
if (typeof first.number !== "number" || !Number.isInteger(first.number) || first.number <= 0)
|
|
55060
|
+
throw new Error("gh pr list returned a pull request without a number");
|
|
55061
|
+
return { number: first.number, ...typeof first.url === "string" && first.url !== "" ? { url: first.url } : {} };
|
|
55062
|
+
}
|
|
55063
|
+
function parsePublisherCommandOutput(stdout) {
|
|
55064
|
+
let parsed;
|
|
55065
|
+
try {
|
|
55066
|
+
parsed = JSON.parse(stdout.trim());
|
|
55067
|
+
} catch {
|
|
55068
|
+
return {};
|
|
55069
|
+
}
|
|
55070
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
55071
|
+
return {};
|
|
55072
|
+
const value = parsed;
|
|
55073
|
+
return {
|
|
55074
|
+
...typeof value.url === "string" && value.url !== "" ? { url: value.url } : {},
|
|
55075
|
+
...typeof value.number === "number" && Number.isInteger(value.number) && value.number > 0 ? { number: value.number } : {}
|
|
55076
|
+
};
|
|
55077
|
+
}
|
|
55078
|
+
var nodeProjectionPublisherExec = (file, args, options) => new Promise((resolvePromise, rejectPromise) => {
|
|
55079
|
+
execFile2(file, args, { cwd: options.cwd, env: options.env, timeout: options.timeoutMs, encoding: "utf8", maxBuffer: 16 * 1024 * 1024 }, (error, stdout, stderr) => {
|
|
55080
|
+
if (error) {
|
|
55081
|
+
rejectPromise(Object.assign(new Error(`${[file, ...args.slice(0, 2)].join(" ")} failed: ${(stderr ?? "").trim() || error.message}`), { code: error.code }));
|
|
55082
|
+
return;
|
|
55083
|
+
}
|
|
55084
|
+
resolvePromise({ stdout });
|
|
55085
|
+
});
|
|
55086
|
+
});
|
|
55087
|
+
function stringEnv(env) {
|
|
55088
|
+
return Object.fromEntries(Object.entries(env).filter((entry) => typeof entry[1] === "string"));
|
|
55089
|
+
}
|
|
55090
|
+
function projectionWriterTargetIssue(repositoryRoot, input) {
|
|
55091
|
+
const issue = refNameIssue(repositoryRoot, "--base", input.base) ?? refNameIssue(repositoryRoot, "--branch", input.branch) ?? remoteNameIssue(input.remote);
|
|
55092
|
+
if (issue)
|
|
55093
|
+
return issue;
|
|
55094
|
+
if (!input.branch.startsWith(PROJECTION_WRITER_BRANCH_PREFIX))
|
|
55095
|
+
return `projection publish --branch must start with ${PROJECTION_WRITER_BRANCH_PREFIX}; the writer never pushes to any other ref`;
|
|
55096
|
+
if (input.branch === input.base)
|
|
55097
|
+
return "projection publish --branch must differ from --base";
|
|
55098
|
+
if (!gitSucceeds2(repositoryRoot, ["remote", "get-url", "--", input.remote]))
|
|
55099
|
+
return `projection publish --remote names no configured remote: ${input.remote}`;
|
|
55100
|
+
return;
|
|
55101
|
+
}
|
|
55102
|
+
function refNameIssue(repositoryRoot, flag, name) {
|
|
55103
|
+
if (name === "" || name.startsWith("-") || name.includes("@{") || !gitSucceeds2(repositoryRoot, ["check-ref-format", "--branch", name])) {
|
|
55104
|
+
return `${flag} is not a valid branch name: ${JSON.stringify(name)}`;
|
|
55105
|
+
}
|
|
55106
|
+
return;
|
|
55107
|
+
}
|
|
55108
|
+
function remoteNameIssue(remote) {
|
|
55109
|
+
if (remote === "" || remote.startsWith("-") || !/^[A-Za-z0-9._-]+$/.test(remote))
|
|
55110
|
+
return `--remote is not a valid remote name: ${JSON.stringify(remote)}`;
|
|
55111
|
+
return;
|
|
55112
|
+
}
|
|
55113
|
+
function acquireProjectionWriterLock(lockPath, startedAt) {
|
|
55114
|
+
let ownership;
|
|
55115
|
+
try {
|
|
55116
|
+
ownership = acquireLocalStoreWriterOwnership(`${lockPath}.coordination`);
|
|
55117
|
+
} catch (error) {
|
|
55118
|
+
if (error instanceof FailureError && error.reasonCode === "runtime-writer-owned") {
|
|
55119
|
+
const holder = readProjectionWriterLock(lockPath);
|
|
55120
|
+
if (holder && isProcessAlive(holder.pid))
|
|
55121
|
+
return { kind: "busy", pid: holder.pid };
|
|
55122
|
+
throw new ProjectionWriterFailure("AC_PRECONDITION_FAILED", "projection-writer-lock-unavailable", "another writer holds the OS lock; its diagnostic owner record is unavailable");
|
|
55123
|
+
}
|
|
55124
|
+
throw error;
|
|
55125
|
+
}
|
|
55126
|
+
try {
|
|
55127
|
+
const body = readPrivateControlFile(lockPath);
|
|
55128
|
+
if (body === undefined && existsSync29(lockPath)) {
|
|
55129
|
+
throw new ProjectionWriterFailure("AC_PRECONDITION_FAILED", "projection-writer-lock-unverified", "the writer owner record has unverified permissions");
|
|
55130
|
+
}
|
|
55131
|
+
const holder = body === undefined ? undefined : parseProjectionWriterLock(body);
|
|
55132
|
+
if (holder && isProcessAlive(holder.pid)) {
|
|
55133
|
+
ownership.release();
|
|
55134
|
+
return { kind: "busy", pid: holder.pid };
|
|
55135
|
+
}
|
|
55136
|
+
rmSync15(lockPath, { force: true });
|
|
55137
|
+
const record = JSON.stringify({ pid: process.pid, startedAt, token: randomUUID3() }, null, 2);
|
|
55138
|
+
createPrivateControlFile(lockPath, record);
|
|
55139
|
+
return { kind: "acquired", record, release() {
|
|
55140
|
+
try {
|
|
55141
|
+
if (readPrivateControlFile(lockPath) === record)
|
|
55142
|
+
rmSync15(lockPath, { force: true });
|
|
55143
|
+
} finally {
|
|
55144
|
+
ownership.release();
|
|
55145
|
+
}
|
|
55146
|
+
} };
|
|
55147
|
+
} catch (error) {
|
|
55148
|
+
ownership.release();
|
|
55149
|
+
throw error;
|
|
55150
|
+
}
|
|
55151
|
+
}
|
|
55152
|
+
function assertProjectionWriterOwner(lockPath, record) {
|
|
55153
|
+
if (readPrivateControlFile(lockPath) !== record) {
|
|
55154
|
+
throw new ProjectionWriterFailure("AC_PRECONDITION_FAILED", "projection-writer-lock-lost", "the writer owner record changed; publication stopped");
|
|
55155
|
+
}
|
|
55156
|
+
}
|
|
55157
|
+
function readProjectionWriterLock(lockPath) {
|
|
55158
|
+
const body = readPrivateControlFile(lockPath);
|
|
55159
|
+
return body === undefined ? undefined : parseProjectionWriterLock(body);
|
|
55160
|
+
}
|
|
55161
|
+
function parseProjectionWriterLock(body) {
|
|
55162
|
+
try {
|
|
55163
|
+
const value = JSON.parse(body);
|
|
55164
|
+
if (typeof value.pid !== "number" || !Number.isInteger(value.pid) || value.pid <= 0)
|
|
55165
|
+
return;
|
|
55166
|
+
return { pid: value.pid, startedAt: typeof value.startedAt === "string" ? value.startedAt : "" };
|
|
55167
|
+
} catch {
|
|
55168
|
+
return;
|
|
55169
|
+
}
|
|
55170
|
+
}
|
|
55171
|
+
function emptyProjectionWriterState(remote, base) {
|
|
55172
|
+
return {
|
|
55173
|
+
schemaVersion: PROJECTION_WRITER_STATE_SCHEMA_VERSION,
|
|
55174
|
+
remote,
|
|
55175
|
+
base,
|
|
55176
|
+
branch: null,
|
|
55177
|
+
runSequence: 0,
|
|
55178
|
+
lastProcessedSha: null,
|
|
55179
|
+
lastSuccessfulSha: null,
|
|
55180
|
+
lastSuccessfulCommit: null,
|
|
55181
|
+
lastSuccessfulBranch: null,
|
|
55182
|
+
lastSuccessfulPublisher: null,
|
|
55183
|
+
lastSuccessfulFiles: [],
|
|
55184
|
+
publisherPending: null,
|
|
55185
|
+
lastRun: null
|
|
55186
|
+
};
|
|
55187
|
+
}
|
|
55188
|
+
function readProjectionWriterState(path, remote, base) {
|
|
55189
|
+
if (!existsSync29(path))
|
|
55190
|
+
return emptyProjectionWriterState(remote, base);
|
|
55191
|
+
let value;
|
|
55192
|
+
try {
|
|
55193
|
+
value = JSON.parse(readFileSync27(path, "utf8"));
|
|
55194
|
+
} catch (error) {
|
|
55195
|
+
throw new ProjectionWriterFailure("AC_PRECONDITION_FAILED", "projection-writer-state-invalid", `projection writer state is unreadable at ${path}: ${errorMessage2(error)}`);
|
|
55196
|
+
}
|
|
55197
|
+
if (value?.schemaVersion !== PROJECTION_WRITER_STATE_SCHEMA_VERSION || value.remote !== remote || value.base !== base || typeof value.runSequence !== "number") {
|
|
55198
|
+
throw new ProjectionWriterFailure("AC_PRECONDITION_FAILED", "projection-writer-state-invalid", `projection writer state at ${path} does not belong to ${remote}/${base}`);
|
|
55199
|
+
}
|
|
55200
|
+
return { ...emptyProjectionWriterState(remote, base), ...value };
|
|
55201
|
+
}
|
|
55202
|
+
function writeProjectionWriterState(path, state) {
|
|
55203
|
+
const temporary = `${path}.${process.pid}.tmp`;
|
|
55204
|
+
writeFileSync11(temporary, `${JSON.stringify(state, null, 2)}
|
|
55205
|
+
`, { encoding: "utf8", mode: 384 });
|
|
55206
|
+
renameSync7(temporary, path);
|
|
55207
|
+
}
|
|
55208
|
+
function prepareProjectionWriterWorktree(repositoryRoot, worktree, sha) {
|
|
55209
|
+
git(repositoryRoot, ["worktree", "prune"]);
|
|
55210
|
+
if (!isRegisteredWorktree(repositoryRoot, worktree)) {
|
|
55211
|
+
rmSync15(worktree, { recursive: true, force: true });
|
|
55212
|
+
git(repositoryRoot, ["worktree", "add", "--detach", worktree, sha]);
|
|
55213
|
+
}
|
|
55214
|
+
git(worktree, ["checkout", "-q", "--force", "--detach", sha]);
|
|
55215
|
+
git(worktree, ["reset", "-q", "--hard", sha]);
|
|
55216
|
+
git(worktree, ["clean", "-ffdq"]);
|
|
55217
|
+
}
|
|
55218
|
+
function isRegisteredWorktree(repositoryRoot, worktree) {
|
|
55219
|
+
if (!existsSync29(join23(worktree, ".git")))
|
|
55220
|
+
return false;
|
|
55221
|
+
const wanted = canonical(worktree);
|
|
55222
|
+
return git(repositoryRoot, ["worktree", "list", "--porcelain", "-z"]).split("\x00").filter((line) => line.startsWith("worktree ")).some((line) => canonical(line.slice("worktree ".length)) === wanted);
|
|
55223
|
+
}
|
|
55224
|
+
function canonical(path) {
|
|
55225
|
+
try {
|
|
55226
|
+
return realpathSync12.native(path);
|
|
55227
|
+
} catch {
|
|
55228
|
+
return resolve30(path);
|
|
55229
|
+
}
|
|
55230
|
+
}
|
|
55231
|
+
function worktreeChangedPaths(worktree) {
|
|
55232
|
+
const tokens = git(worktree, ["status", "--porcelain=v1", "-z", "--untracked-files=all"]).split("\x00");
|
|
55233
|
+
const paths = new Set;
|
|
55234
|
+
for (let index = 0;index < tokens.length; index += 1) {
|
|
55235
|
+
const entry = tokens[index];
|
|
55236
|
+
if (entry.length < 4)
|
|
55237
|
+
continue;
|
|
55238
|
+
paths.add(entry.slice(3));
|
|
55239
|
+
if (entry[0] === "R" || entry[0] === "C") {
|
|
55240
|
+
const source = tokens[index + 1];
|
|
55241
|
+
if (source)
|
|
55242
|
+
paths.add(source);
|
|
55243
|
+
index += 1;
|
|
55244
|
+
}
|
|
55245
|
+
}
|
|
55246
|
+
return [...paths].sort();
|
|
55247
|
+
}
|
|
55248
|
+
function isProjectionOwnedPath(path, patterns) {
|
|
55249
|
+
return patterns.some((pattern) => pattern.endsWith("/**") ? path.startsWith(pattern.slice(0, -2)) : path === pattern);
|
|
55250
|
+
}
|
|
55251
|
+
function carriesSameProjection(worktree, remoteTip, commitSha, baseSha) {
|
|
55252
|
+
if (!gitSucceeds2(worktree, ["cat-file", "-e", `${remoteTip}^{commit}`]))
|
|
55253
|
+
return false;
|
|
55254
|
+
const parents = git(worktree, ["rev-list", "--parents", "-n", "1", remoteTip]).trim().split(/\s+/).slice(1);
|
|
55255
|
+
if (parents.length !== 1 || parents[0] !== baseSha)
|
|
55256
|
+
return false;
|
|
55257
|
+
return git(worktree, ["rev-parse", `${remoteTip}^{tree}`]).trim() === git(worktree, ["rev-parse", `${commitSha}^{tree}`]).trim();
|
|
55258
|
+
}
|
|
55259
|
+
function lsRemoteBranch(repositoryRoot, remote, branch, env) {
|
|
55260
|
+
const line = remoteGit(repositoryRoot, ["ls-remote", "--", remote, `refs/heads/${branch}`], env).split(`
|
|
55261
|
+
`).find((entry) => entry.endsWith(` refs/heads/${branch}`));
|
|
55262
|
+
return line ? line.split("\t")[0] : null;
|
|
55263
|
+
}
|
|
55264
|
+
function remoteGit(cwd, args, env) {
|
|
55265
|
+
return withLocalEgress2("projection-publishing", () => git(cwd, args, { GIT_TERMINAL_PROMPT: "0" }), env);
|
|
55266
|
+
}
|
|
55267
|
+
function git(cwd, args, extraEnv = {}) {
|
|
55268
|
+
const result = spawnSync5("git", args, {
|
|
55269
|
+
cwd,
|
|
55270
|
+
encoding: "utf8",
|
|
55271
|
+
env: { ...process.env, ...extraEnv },
|
|
55272
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
55273
|
+
maxBuffer: 256 * 1024 * 1024
|
|
55274
|
+
});
|
|
55275
|
+
if (result.error)
|
|
55276
|
+
throw new ProjectionWriterFailure("AC_PRECONDITION_FAILED", "projection-writer-git-failed", `git ${args[0]} could not run: ${result.error.message}`);
|
|
55277
|
+
if (result.status !== 0) {
|
|
55278
|
+
throw new ProjectionWriterFailure("AC_PRECONDITION_FAILED", "projection-writer-git-failed", `git ${args[0]} failed: ${(result.stderr ?? "").trim() || `exit ${result.status}`}`);
|
|
55279
|
+
}
|
|
55280
|
+
return result.stdout ?? "";
|
|
55281
|
+
}
|
|
55282
|
+
function gitSucceeds2(cwd, args) {
|
|
55283
|
+
return spawnSync5("git", args, { cwd, stdio: ["ignore", "ignore", "ignore"] }).status === 0;
|
|
55284
|
+
}
|
|
55285
|
+
function envelopeFailure(envelope, fallbackReason) {
|
|
55286
|
+
const error = envelope.error;
|
|
55287
|
+
return new ProjectionWriterFailure(error?.code ?? "AC_UNCLASSIFIED_FAILURE", error?.reasonCode ?? fallbackReason, error?.message ?? fallbackReason);
|
|
55288
|
+
}
|
|
55289
|
+
function asWriterFailure(error) {
|
|
55290
|
+
if (error instanceof ProjectionWriterFailure)
|
|
55291
|
+
return error;
|
|
55292
|
+
if (error instanceof LocalEgressPolicyError2)
|
|
55293
|
+
return new ProjectionWriterFailure("AC_POLICY_VIOLATION", "egress-policy-denied", error.message);
|
|
55294
|
+
const envelope = publicOperationErrorEnvelope(PROJECTION_PUBLISH_REQUEST_ID, error);
|
|
55295
|
+
return envelopeFailure(envelope, "projection-writer-failed");
|
|
55296
|
+
}
|
|
55297
|
+
function writerError(failure) {
|
|
55298
|
+
return { code: failure.code, reasonCode: failure.reasonCode, message: failure.message };
|
|
55299
|
+
}
|
|
55300
|
+
function errorMessage2(error) {
|
|
55301
|
+
return error instanceof Error ? error.message : String(error);
|
|
55302
|
+
}
|
|
54154
55303
|
// packages/local-runtime/runtime-daemon/src/capability-dependencies.ts
|
|
54155
55304
|
init_src();
|
|
54156
55305
|
var CODEGRAPH_BINARY2 = "codegraph";
|
|
@@ -54274,8 +55423,8 @@ function runFlowSelectorCheckCommand(cwd, options = {}) {
|
|
|
54274
55423
|
}
|
|
54275
55424
|
}
|
|
54276
55425
|
// packages/local-runtime/runtime-daemon/src/recommendations.ts
|
|
54277
|
-
import { existsSync as
|
|
54278
|
-
import { join as
|
|
55426
|
+
import { existsSync as existsSync30 } from "node:fs";
|
|
55427
|
+
import { join as join24 } from "node:path";
|
|
54279
55428
|
|
|
54280
55429
|
// packages/local-runtime/runtime-daemon/src/refactor-verify.ts
|
|
54281
55430
|
init_src();
|
|
@@ -54877,7 +56026,7 @@ class RecommendationsService {
|
|
|
54877
56026
|
});
|
|
54878
56027
|
}
|
|
54879
56028
|
readGitOnlyDecision(repositoryRoot, recommendationId, surface) {
|
|
54880
|
-
if (!
|
|
56029
|
+
if (!existsSync30(join24(repositoryRoot, REFACTOR_DECISION_DIRECTORY)))
|
|
54881
56030
|
return;
|
|
54882
56031
|
let decisions;
|
|
54883
56032
|
try {
|
|
@@ -55547,7 +56696,7 @@ function lifecycleEnvelope(input) {
|
|
|
55547
56696
|
});
|
|
55548
56697
|
}
|
|
55549
56698
|
function refactorDecisionsInPlay(repositoryRoot, records) {
|
|
55550
|
-
return records.some(isRefactorRecommendation) ||
|
|
56699
|
+
return records.some(isRefactorRecommendation) || existsSync30(join24(repositoryRoot, REFACTOR_DECISION_DIRECTORY));
|
|
55551
56700
|
}
|
|
55552
56701
|
function ledgerAuditDecisionRefused(surface, record, recommendations, decisions) {
|
|
55553
56702
|
let head = record.recommendationId;
|
|
@@ -55668,15 +56817,15 @@ function recommendationActorKind(input) {
|
|
|
55668
56817
|
// packages/local-runtime/runtime-daemon/src/changeset-authority.ts
|
|
55669
56818
|
init_src();
|
|
55670
56819
|
init_src();
|
|
55671
|
-
import { existsSync as
|
|
55672
|
-
import { resolve as
|
|
56820
|
+
import { existsSync as existsSync31, readFileSync as readFileSync29 } from "node:fs";
|
|
56821
|
+
import { resolve as resolve32 } from "node:path";
|
|
55673
56822
|
init_src();
|
|
55674
56823
|
|
|
55675
56824
|
// packages/local-runtime/runtime-daemon/src/committed-change-acceptance.ts
|
|
55676
56825
|
init_src();
|
|
55677
56826
|
import { execFileSync as execFileSync10 } from "node:child_process";
|
|
55678
|
-
import { closeSync as
|
|
55679
|
-
import { resolve as
|
|
56827
|
+
import { closeSync as closeSync10, constants as fsConstants3, fstatSync as fstatSync4, lstatSync as lstatSync15, openSync as openSync11, readdirSync as readdirSync15, readFileSync as readFileSync28 } from "node:fs";
|
|
56828
|
+
import { resolve as resolve31 } from "node:path";
|
|
55680
56829
|
init_src();
|
|
55681
56830
|
var SEMANTIC_MODEL_DIRECTORIES = ["nodes", "relations", "flows"];
|
|
55682
56831
|
var SEMANTIC_MODEL_PATH = /^\.archcontext\/model\/(nodes|relations|flows)\/[^/]+\.ya?ml$/;
|
|
@@ -55741,7 +56890,7 @@ function readSemanticModelSnapshot(root, readEntry) {
|
|
|
55741
56890
|
}
|
|
55742
56891
|
for (const entry of entries.filter((name) => /\.ya?ml$/.test(name)).sort()) {
|
|
55743
56892
|
const path = `${relativeDirectory}/${entry}`;
|
|
55744
|
-
bodies.set(path, strictUtf8(readEntry(
|
|
56893
|
+
bodies.set(path, strictUtf8(readEntry(resolve31(absolute, entry), path), path));
|
|
55745
56894
|
}
|
|
55746
56895
|
}
|
|
55747
56896
|
const hashes = new Map([...bodies].map(([path, body]) => [path, digestJson({ body })]));
|
|
@@ -55750,13 +56899,13 @@ function readSemanticModelSnapshot(root, readEntry) {
|
|
|
55750
56899
|
function readRegularFileBytes(absolute, path) {
|
|
55751
56900
|
if (!lstatSync15(absolute).isFile())
|
|
55752
56901
|
throw new FailureError("AC_PRECONDITION_FAILED", "semantic-model-entry-not-regular");
|
|
55753
|
-
const fd =
|
|
56902
|
+
const fd = openSync11(absolute, fsConstants3.O_RDONLY | (fsConstants3.O_NOFOLLOW ?? 0) | (fsConstants3.O_NONBLOCK ?? 0));
|
|
55754
56903
|
try {
|
|
55755
56904
|
if (!fstatSync4(fd).isFile())
|
|
55756
56905
|
throw new FailureError("AC_PRECONDITION_FAILED", "semantic-model-entry-not-regular");
|
|
55757
|
-
return
|
|
56906
|
+
return readFileSync28(fd);
|
|
55758
56907
|
} finally {
|
|
55759
|
-
|
|
56908
|
+
closeSync10(fd);
|
|
55760
56909
|
}
|
|
55761
56910
|
}
|
|
55762
56911
|
function strictUtf8(bytes, path) {
|
|
@@ -56254,10 +57403,10 @@ class ChangeSetAuthorityService {
|
|
|
56254
57403
|
return publicFailureEnvelope("practices.waive", "AC_SCHEMA_INVALID", "waiver-input-invalid");
|
|
56255
57404
|
}
|
|
56256
57405
|
const path = `.archcontext/waivers/${waiverId}.json`;
|
|
56257
|
-
const absolute =
|
|
57406
|
+
const absolute = resolve32(session.workspace.root, path);
|
|
56258
57407
|
const body = `${JSON.stringify(waiver, null, 2)}
|
|
56259
57408
|
`;
|
|
56260
|
-
const expectedHash =
|
|
57409
|
+
const expectedHash = existsSync31(absolute) ? digestJson({ body: readFileSync29(absolute, "utf8") }) : "missing";
|
|
56261
57410
|
const draft = this.changeSetEngine.plan({
|
|
56262
57411
|
id: input.id ?? `changeset.practice-waiver-${waiverId.replace(/[^A-Za-z0-9_-]/g, "-")}`,
|
|
56263
57412
|
base: {
|
|
@@ -56409,7 +57558,7 @@ class ChangeSetAuthorityService {
|
|
|
56409
57558
|
if (draft.base.worktreeDigest !== current)
|
|
56410
57559
|
throw new FailureError("AC_PRECONDITION_FAILED", "apply-worktree-changed", { expectedWorktreeDigest: draft.base.worktreeDigest, actualWorktreeDigest: current });
|
|
56411
57560
|
const currentModel = await this.readModelStore.validateModel(session.workspace);
|
|
56412
|
-
const baseErrors =
|
|
57561
|
+
const baseErrors = changeSetBaseModelBlockingErrors(currentModel, draft);
|
|
56413
57562
|
if (baseErrors.length > 0) {
|
|
56414
57563
|
throw new FailureError("AC_PRECONDITION_FAILED", "apply-model-invalid", { issueCount: baseErrors.length });
|
|
56415
57564
|
}
|
|
@@ -56693,7 +57842,7 @@ function architectureLedgerWriteAppendsEvents(mode) {
|
|
|
56693
57842
|
// packages/local-runtime/runtime-daemon/src/rpc-server.ts
|
|
56694
57843
|
init_src();
|
|
56695
57844
|
import { createServer as createServer2 } from "node:http";
|
|
56696
|
-
import { closeSync as
|
|
57845
|
+
import { closeSync as closeSync11, existsSync as existsSync32, mkdirSync as mkdirSync13 } from "node:fs";
|
|
56697
57846
|
import { dirname as dirname14 } from "node:path";
|
|
56698
57847
|
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
56699
57848
|
var DEFAULT_DAEMON_IDLE_TIMEOUT_MS = 30 * 60000;
|
|
@@ -56751,7 +57900,7 @@ class ArchctxRuntimeRpcServer {
|
|
|
56751
57900
|
if (connection)
|
|
56752
57901
|
await removePrivateControlFile(connection.connectionPath);
|
|
56753
57902
|
if (this.lockFd !== undefined) {
|
|
56754
|
-
|
|
57903
|
+
closeSync11(this.lockFd);
|
|
56755
57904
|
this.lockFd = undefined;
|
|
56756
57905
|
await removePrivateControlFile(this.options.lockPath ?? defaultDaemonLockPath(this.options.root ?? process.cwd()));
|
|
56757
57906
|
}
|
|
@@ -56764,8 +57913,8 @@ class ArchctxRuntimeRpcServer {
|
|
|
56764
57913
|
const root = this.options.root ?? process.cwd();
|
|
56765
57914
|
const connectionPath = this.options.connectionPath ?? defaultDaemonConnectionPath(root);
|
|
56766
57915
|
const lockPath = this.options.lockPath ?? defaultDaemonLockPath(root);
|
|
56767
|
-
|
|
56768
|
-
|
|
57916
|
+
mkdirSync13(dirname14(connectionPath), { recursive: true });
|
|
57917
|
+
mkdirSync13(dirname14(lockPath), { recursive: true });
|
|
56769
57918
|
this.lockFd = acquireDaemonLock(lockPath, root);
|
|
56770
57919
|
const token = this.options.token ?? randomBytes5(18).toString("base64url");
|
|
56771
57920
|
const server = createServer2((request, response) => {
|
|
@@ -56816,7 +57965,7 @@ class ArchctxRuntimeRpcServer {
|
|
|
56816
57965
|
await removePrivateControlFile(connection.connectionPath);
|
|
56817
57966
|
} finally {
|
|
56818
57967
|
if (this.lockFd !== undefined)
|
|
56819
|
-
|
|
57968
|
+
closeSync11(this.lockFd);
|
|
56820
57969
|
this.lockFd = undefined;
|
|
56821
57970
|
}
|
|
56822
57971
|
if (connection)
|
|
@@ -56858,7 +58007,7 @@ class ArchctxRuntimeRpcServer {
|
|
|
56858
58007
|
});
|
|
56859
58008
|
if (this.server && this.connection && (this.inFlightRpcRequests > 0 || this.authorizedRequests !== requestsBeforeRemoval)) {
|
|
56860
58009
|
try {
|
|
56861
|
-
if (!
|
|
58010
|
+
if (!existsSync32(connection.connectionPath))
|
|
56862
58011
|
createPrivateControlFile(connection.connectionPath, JSON.stringify(connection, null, 2));
|
|
56863
58012
|
this.idleExitPending = false;
|
|
56864
58013
|
this.armIdleTimer();
|
|
@@ -57071,8 +58220,8 @@ async function readRpcRequestBody(request, limits) {
|
|
|
57071
58220
|
}
|
|
57072
58221
|
|
|
57073
58222
|
// packages/local-runtime/runtime-daemon/src/index.ts
|
|
57074
|
-
import { existsSync as
|
|
57075
|
-
import { resolve as
|
|
58223
|
+
import { existsSync as existsSync33, readFileSync as readFileSync30 } from "node:fs";
|
|
58224
|
+
import { resolve as resolve33 } from "node:path";
|
|
57076
58225
|
init_src();
|
|
57077
58226
|
|
|
57078
58227
|
// packages/local-runtime/runtime-daemon/src/egress.ts
|
|
@@ -57108,6 +58257,8 @@ function effectiveEgressChannels2(env, live) {
|
|
|
57108
58257
|
}
|
|
57109
58258
|
if (live.updateCheckRequested)
|
|
57110
58259
|
channels.push({ channel: "npm-update-check", destination: "configured npm registry", trigger: "explicit update --check or doctor --check-updates", data: "archctx package version lookup", status: "enabled" });
|
|
58260
|
+
if (live.projectionPublishRequested)
|
|
58261
|
+
channels.push({ channel: "projection-publishing", destination: "configured git remote, plus GitHub via gh or the operator's publisher command", trigger: "explicit archctx projection publish", data: "one commit of generated projection outputs and a pull request title/body listing their paths", status: "enabled" });
|
|
57111
58262
|
if (env[CODEGRAPH_TELEMETRY_ENV] !== undefined && env[CODEGRAPH_TELEMETRY_ENV] !== CODEGRAPH_TELEMETRY_DISABLED_VALUE)
|
|
57112
58263
|
channels.push({ channel: "codegraph-telemetry", destination: "CodeGraph configured telemetry endpoint", trigger: "CodeGraph execution with telemetry enabled", data: "third-party telemetry", status: "enabled" });
|
|
57113
58264
|
return channels.map((channel) => localEgressAdmission2(channel.channel, env).allowed ? channel : { ...channel, status: "blocked-by-policy" });
|
|
@@ -57262,7 +58413,7 @@ class ArchctxDaemon {
|
|
|
57262
58413
|
const model = loadNativeModelFromArchContext(root);
|
|
57263
58414
|
const canonical = renderAgentContextProjection({ model, sourceDigest: digestJson({ model }), existingFiles: loadAgentContextProjectionFiles(root, model) });
|
|
57264
58415
|
const canonicalBody = canonical.files.find((candidate) => candidate.path === file.path)?.body;
|
|
57265
|
-
if (canonicalBody === undefined || projectionOwnedWriteBody(canonicalBody,
|
|
58416
|
+
if (canonicalBody === undefined || projectionOwnedWriteBody(canonicalBody, readFileSync30(resolve33(root, file.path), "utf8")) !== file.body) {
|
|
57266
58417
|
throw new Error(`Root contract requires canonical marker-only content: ${file.path}`);
|
|
57267
58418
|
}
|
|
57268
58419
|
}
|
|
@@ -57521,7 +58672,7 @@ class ArchctxDaemon {
|
|
|
57521
58672
|
if (this.auditRunAbortControllers.size > 0)
|
|
57522
58673
|
return true;
|
|
57523
58674
|
for (const [repositoryId, session] of this.sessions) {
|
|
57524
|
-
if (!
|
|
58675
|
+
if (!existsSync33(session.workspace.root)) {
|
|
57525
58676
|
this.sessions.delete(repositoryId);
|
|
57526
58677
|
continue;
|
|
57527
58678
|
}
|
|
@@ -57684,6 +58835,10 @@ class ArchctxDaemon {
|
|
|
57684
58835
|
async completeTaskUnchecked(root, input) {
|
|
57685
58836
|
this.assertRunning();
|
|
57686
58837
|
assertNoCallerProvidedAttestationFields(input, "complete-task");
|
|
58838
|
+
if (this.codeFacts.assertCanSync) {
|
|
58839
|
+
root = findRepositoryRoot(root);
|
|
58840
|
+
this.codeFacts.assertCanSync(root);
|
|
58841
|
+
}
|
|
57687
58842
|
const session = await this.openSession(root);
|
|
57688
58843
|
let currentHeadSha = input.headSha;
|
|
57689
58844
|
try {
|
|
@@ -57721,7 +58876,7 @@ class ArchctxDaemon {
|
|
|
57721
58876
|
try {
|
|
57722
58877
|
projectionDrift = completeTaskProjectionDrift(session.workspace.root);
|
|
57723
58878
|
projectionFreshness = completeTaskProjectionFreshness(session.workspace.root);
|
|
57724
|
-
if (model.valid || !
|
|
58879
|
+
if (model.valid || !existsSync33(resolve33(session.workspace.root, ".archcontext/manifest.yaml"))) {
|
|
57725
58880
|
architectureDebt = readCompleteTaskArchitectureDebt(session.workspace.root, practiceEnforcement);
|
|
57726
58881
|
}
|
|
57727
58882
|
} catch (error) {
|
|
@@ -58210,7 +59365,7 @@ class ArchctxDaemon {
|
|
|
58210
59365
|
}
|
|
58211
59366
|
async restoreRepositorySessions() {
|
|
58212
59367
|
for (const record of await this.localStore.listRepositorySessions()) {
|
|
58213
|
-
if (!record.root || !
|
|
59368
|
+
if (!record.root || !existsSync33(record.root))
|
|
58214
59369
|
continue;
|
|
58215
59370
|
if (repositoryFingerprint(record.root) !== record.repositoryId)
|
|
58216
59371
|
continue;
|
|
@@ -58508,7 +59663,7 @@ function blockedProductionInjections(deps) {
|
|
|
58508
59663
|
].filter((key) => (key in deps));
|
|
58509
59664
|
}
|
|
58510
59665
|
function completeTaskProjectionDrift(root) {
|
|
58511
|
-
if (!
|
|
59666
|
+
if (!existsSync33(resolve33(root, "docs/architecture/.projection-manifest.json")))
|
|
58512
59667
|
return;
|
|
58513
59668
|
const profile = loadArchitectureDocumentationProfile(root);
|
|
58514
59669
|
const loaded = loadArchitectureDocumentationInputs(root, profile);
|
|
@@ -58554,10 +59709,11 @@ function completeTaskProjectionFreshness(root) {
|
|
|
58554
59709
|
}
|
|
58555
59710
|
|
|
58556
59711
|
// packages/local-runtime/codegraph-adapter/src/index.ts
|
|
59712
|
+
init_src();
|
|
58557
59713
|
import { execFileSync as execFileSync11 } from "node:child_process";
|
|
58558
|
-
import { accessSync as accessSync2, closeSync as
|
|
59714
|
+
import { accessSync as accessSync2, closeSync as closeSync12, constants as fsConstants4, existsSync as existsSync34, openSync as openSync12, readFileSync as readFileSync31, readSync as readSync6, realpathSync as realpathSync13, statSync as statSync10 } from "node:fs";
|
|
58559
59715
|
import { createRequire as createRequire5 } from "node:module";
|
|
58560
|
-
import { basename as basename7, delimiter as delimiter2, dirname as dirname15, isAbsolute as isAbsolute10, join as
|
|
59716
|
+
import { basename as basename7, delimiter as delimiter2, dirname as dirname15, isAbsolute as isAbsolute10, join as join26, posix as posix4, resolve as resolve34 } from "node:path";
|
|
58561
59717
|
init_src();
|
|
58562
59718
|
var REQUIRED_CODEGRAPH_PACKAGE2 = "@colbymchenry/codegraph";
|
|
58563
59719
|
var REQUIRED_CODEGRAPH_VERSION2 = productVersionManifest().runtime.codeGraph.requiredVersion;
|
|
@@ -58594,15 +59750,15 @@ function codeGraphCliInvocation2(binary, cwd, pathValue = process.env.PATH ?? ""
|
|
|
58594
59750
|
}
|
|
58595
59751
|
function resolveExecutable2(binary, cwd, pathValue) {
|
|
58596
59752
|
if (binary.includes("/") || binary.includes("\\")) {
|
|
58597
|
-
const path = isAbsolute10(binary) ? binary :
|
|
58598
|
-
return
|
|
59753
|
+
const path = isAbsolute10(binary) ? binary : join26(cwd, binary);
|
|
59754
|
+
return existsSync34(path) ? realpathSync13.native(path) : undefined;
|
|
58599
59755
|
}
|
|
58600
59756
|
for (const dir of pathValue.split(delimiter2)) {
|
|
58601
59757
|
if (!dir)
|
|
58602
59758
|
continue;
|
|
58603
|
-
const path =
|
|
59759
|
+
const path = join26(dir, binary);
|
|
58604
59760
|
if (isExecutablePath2(path))
|
|
58605
|
-
return
|
|
59761
|
+
return realpathSync13.native(path);
|
|
58606
59762
|
}
|
|
58607
59763
|
return;
|
|
58608
59764
|
}
|
|
@@ -58620,12 +59776,12 @@ function isExecutablePath2(path) {
|
|
|
58620
59776
|
function resolvePackagedCodeGraphShim2() {
|
|
58621
59777
|
try {
|
|
58622
59778
|
const manifestPath = requireFromAdapter2.resolve(`${REQUIRED_CODEGRAPH_PACKAGE2}/package.json`);
|
|
58623
|
-
const manifest = JSON.parse(
|
|
59779
|
+
const manifest = JSON.parse(readFileSync31(manifestPath, "utf8"));
|
|
58624
59780
|
const relativeBin = manifest.bin?.codegraph;
|
|
58625
59781
|
if (manifest.version !== REQUIRED_CODEGRAPH_VERSION2 || !relativeBin)
|
|
58626
59782
|
return;
|
|
58627
|
-
const shimPath =
|
|
58628
|
-
return
|
|
59783
|
+
const shimPath = resolve34(dirname15(manifestPath), relativeBin);
|
|
59784
|
+
return existsSync34(shimPath) ? realpathSync13.native(shimPath) : undefined;
|
|
58629
59785
|
} catch {
|
|
58630
59786
|
return;
|
|
58631
59787
|
}
|
|
@@ -58639,15 +59795,15 @@ function isNodeRuntimeScript2(path) {
|
|
|
58639
59795
|
function readFilePrefix2(path, length) {
|
|
58640
59796
|
let fd;
|
|
58641
59797
|
try {
|
|
58642
|
-
fd =
|
|
59798
|
+
fd = openSync12(path, "r");
|
|
58643
59799
|
const buffer = Buffer.alloc(length);
|
|
58644
|
-
const bytes =
|
|
59800
|
+
const bytes = readSync6(fd, buffer, 0, length, 0);
|
|
58645
59801
|
return buffer.subarray(0, bytes).toString("utf8");
|
|
58646
59802
|
} catch {
|
|
58647
59803
|
return "";
|
|
58648
59804
|
} finally {
|
|
58649
59805
|
if (fd !== undefined)
|
|
58650
|
-
|
|
59806
|
+
closeSync12(fd);
|
|
58651
59807
|
}
|
|
58652
59808
|
}
|
|
58653
59809
|
var CODEGRAPH_IMPORT_NODE_OUTPUT_BYTES2 = 4 * 1024;
|
|
@@ -58662,7 +59818,7 @@ function readProjectionCodeGraphStatus2(invocation, root, timeout, envOverrides)
|
|
|
58662
59818
|
function projectionCodeGraphStatusIssues2(status, root, phase) {
|
|
58663
59819
|
const issue = (code, text) => ({
|
|
58664
59820
|
code,
|
|
58665
|
-
message: `CodeGraph ${phase} ${text}`
|
|
59821
|
+
message: phase === "repository" ? CODE_FACTS_ISSUE_MESSAGES[code] : `CodeGraph ${phase} ${text}`
|
|
58666
59822
|
});
|
|
58667
59823
|
if (!status.initialized)
|
|
58668
59824
|
return [issue("not-initialized", "status is not initialized")];
|
|
@@ -58685,7 +59841,7 @@ function sameRealPath2(left, right) {
|
|
|
58685
59841
|
if (typeof left !== "string")
|
|
58686
59842
|
return false;
|
|
58687
59843
|
try {
|
|
58688
|
-
return
|
|
59844
|
+
return realpathSync13.native(left) === realpathSync13.native(right);
|
|
58689
59845
|
} catch {
|
|
58690
59846
|
return false;
|
|
58691
59847
|
}
|
|
@@ -58738,7 +59894,7 @@ function inspectRepositoryCodeFacts2(root, options = {}) {
|
|
|
58738
59894
|
message: (error instanceof Error ? error.message : String(error)).replace(/\s+/g, " ").trim().slice(0, 400)
|
|
58739
59895
|
};
|
|
58740
59896
|
}
|
|
58741
|
-
const versionIssues = codeGraphVersion === REQUIRED_CODEGRAPH_VERSION2 ? [] : [{ code: "version-mismatch", message:
|
|
59897
|
+
const versionIssues = codeGraphVersion === REQUIRED_CODEGRAPH_VERSION2 ? [] : [{ code: "version-mismatch", message: CODE_FACTS_ISSUE_MESSAGES["version-mismatch"] }];
|
|
58742
59898
|
if (!indexPresent) {
|
|
58743
59899
|
return { schemaVersion, state: "index-missing", indexPresent: false, requiredVersion: REQUIRED_CODEGRAPH_VERSION2, codeGraphVersion, issues: versionIssues };
|
|
58744
59900
|
}
|
|
@@ -58768,14 +59924,14 @@ function inspectRepositoryCodeFacts2(root, options = {}) {
|
|
|
58768
59924
|
current: status.index?.currentExtractionVersion ?? null
|
|
58769
59925
|
},
|
|
58770
59926
|
indexState: status.index?.state ?? null,
|
|
58771
|
-
reindexRecommended:
|
|
59927
|
+
reindexRecommended: issues.some((issue) => issue.code !== "pending-source-changes"),
|
|
58772
59928
|
pendingRefs: status.index?.pendingRefs ?? null,
|
|
58773
59929
|
pendingChanges: pending ? { added: pending.added, modified: pending.modified, removed: pending.removed } : null,
|
|
58774
59930
|
issues
|
|
58775
59931
|
};
|
|
58776
59932
|
}
|
|
58777
59933
|
function codeGraphIndexAvailable2(workspaceRoot) {
|
|
58778
|
-
return
|
|
59934
|
+
return existsSync34(join26(workspaceRoot, ".codegraph"));
|
|
58779
59935
|
}
|
|
58780
59936
|
var NON_DECLARATION_KINDS2 = new Set(["file", "import", "export", "parameter"]);
|
|
58781
59937
|
var RUNTIME_BUILTIN_CALL_NAMES2 = new Set([
|
|
@@ -59571,6 +60727,95 @@ function isExternalDocumentationResourceUri(uri) {
|
|
|
59571
60727
|
function architectureBookResourceInput(uri) {
|
|
59572
60728
|
return ARCHITECTURE_BOOK_RESOURCES.find((resource) => resource.uri === uri)?.input;
|
|
59573
60729
|
}
|
|
60730
|
+
// schemas/repo/architecture-node.schema.json
|
|
60731
|
+
var architecture_node_schema_default2 = {
|
|
60732
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
60733
|
+
$id: "https://archctx.repoharness.com/schemas/repo/architecture-node.schema.json",
|
|
60734
|
+
title: "ArchitectureNode",
|
|
60735
|
+
type: "object",
|
|
60736
|
+
additionalProperties: false,
|
|
60737
|
+
required: ["schemaVersion", "id", "kind", "name", "status", "summary"],
|
|
60738
|
+
properties: {
|
|
60739
|
+
schemaVersion: { const: "archcontext.node/v2" },
|
|
60740
|
+
id: { type: "string", pattern: "^([a-z][a-z0-9]*(\\.[a-z0-9][a-z0-9-]*)*::)?[a-z][a-z0-9]*(\\.[a-z0-9][a-z0-9-]*)+$" },
|
|
60741
|
+
kind: {
|
|
60742
|
+
type: "string",
|
|
60743
|
+
enum: ["capability", "module", "component", "interface", "event", "datastore", "external-system", "deployment"]
|
|
60744
|
+
},
|
|
60745
|
+
name: { type: "string" },
|
|
60746
|
+
status: { type: "string", enum: ["active", "planned", "deprecated", "removed"] },
|
|
60747
|
+
parent: { type: "string", pattern: "^([a-z][a-z0-9]*(\\.[a-z0-9][a-z0-9-]*)*::)?[a-z][a-z0-9]*(\\.[a-z0-9][a-z0-9-]*)+$" },
|
|
60748
|
+
summary: { type: "string" },
|
|
60749
|
+
responsibilities: { type: "array", items: { type: "string" }, minItems: 1 },
|
|
60750
|
+
source: {
|
|
60751
|
+
type: "object",
|
|
60752
|
+
additionalProperties: false,
|
|
60753
|
+
properties: {
|
|
60754
|
+
include: { type: "array", items: { type: "string" } },
|
|
60755
|
+
exclude: { type: "array", items: { type: "string" } },
|
|
60756
|
+
entrypoints: {
|
|
60757
|
+
type: "array",
|
|
60758
|
+
items: {
|
|
60759
|
+
type: "object",
|
|
60760
|
+
additionalProperties: false,
|
|
60761
|
+
required: ["id", "path", "symbols"],
|
|
60762
|
+
properties: {
|
|
60763
|
+
id: { type: "string", pattern: "^[a-z][a-z0-9-]*(\\.[a-z0-9][a-z0-9-]*)+$" },
|
|
60764
|
+
path: { type: "string", minLength: 1 },
|
|
60765
|
+
symbols: {
|
|
60766
|
+
type: "array",
|
|
60767
|
+
minItems: 1,
|
|
60768
|
+
items: {
|
|
60769
|
+
type: "object",
|
|
60770
|
+
additionalProperties: false,
|
|
60771
|
+
required: ["name", "sinks"],
|
|
60772
|
+
properties: {
|
|
60773
|
+
name: { type: "string", minLength: 1 },
|
|
60774
|
+
sinks: {
|
|
60775
|
+
type: "array",
|
|
60776
|
+
minItems: 1,
|
|
60777
|
+
items: {
|
|
60778
|
+
type: "object",
|
|
60779
|
+
additionalProperties: false,
|
|
60780
|
+
required: ["id", "path", "symbol"],
|
|
60781
|
+
properties: {
|
|
60782
|
+
id: { type: "string", pattern: "^[a-z][a-z0-9-]*(\\.[a-z0-9][a-z0-9-]*)+$" },
|
|
60783
|
+
path: { type: "string", minLength: 1 },
|
|
60784
|
+
symbol: { type: "string", minLength: 1 }
|
|
60785
|
+
}
|
|
60786
|
+
}
|
|
60787
|
+
}
|
|
60788
|
+
}
|
|
60789
|
+
}
|
|
60790
|
+
}
|
|
60791
|
+
}
|
|
60792
|
+
}
|
|
60793
|
+
}
|
|
60794
|
+
}
|
|
60795
|
+
},
|
|
60796
|
+
ownership: {
|
|
60797
|
+
type: "object",
|
|
60798
|
+
additionalProperties: false,
|
|
60799
|
+
properties: {
|
|
60800
|
+
lifecycle: { type: "array", items: { type: "string" } },
|
|
60801
|
+
data: { type: "array", items: { type: "string" } }
|
|
60802
|
+
}
|
|
60803
|
+
},
|
|
60804
|
+
interfaces: {
|
|
60805
|
+
type: "object",
|
|
60806
|
+
additionalProperties: false,
|
|
60807
|
+
properties: {
|
|
60808
|
+
provides: { type: "array", items: { type: "string" } },
|
|
60809
|
+
consumes: { type: "array", items: { type: "string" } }
|
|
60810
|
+
}
|
|
60811
|
+
},
|
|
60812
|
+
criticality: { type: "string", enum: ["low", "medium", "high", "critical"] },
|
|
60813
|
+
riskDomains: { type: "array", items: { type: "string" } },
|
|
60814
|
+
notes: { type: "array", items: { type: "string" } },
|
|
60815
|
+
extensions: { type: "object", additionalProperties: true }
|
|
60816
|
+
}
|
|
60817
|
+
};
|
|
60818
|
+
|
|
59574
60819
|
// packages/surfaces/cli/src/main.ts
|
|
59575
60820
|
var [, , command, ...args] = process.argv;
|
|
59576
60821
|
var CLI_ENTRY = fileURLToPath2(import.meta.url);
|
|
@@ -59611,6 +60856,12 @@ var AUDIT_RUN_VALUE_FLAGS = ["--task-session-id", "--reason", "--risk", "--uncer
|
|
|
59611
60856
|
var AUDIT_RUN_BOOLEAN_FLAGS = ["--no-wait", ...AUDIT_GLOBAL_BOOLEAN_FLAGS];
|
|
59612
60857
|
var AUDIT_APPROVE_VALUE_FLAGS = ["--run-id", "--confirm-public-repo", ...AUDIT_GLOBAL_VALUE_FLAGS];
|
|
59613
60858
|
var AUDIT_APPROVE_BOOLEAN_FLAGS = ["--resume", ...AUDIT_GLOBAL_BOOLEAN_FLAGS];
|
|
60859
|
+
var PROJECTION_PUBLISH_VALUE_FLAGS = ["--base", "--branch", "--interval", "--publisher", "--publisher-command", "--remote"];
|
|
60860
|
+
var PROJECTION_PUBLISH_BOOLEAN_FLAGS = ["--accept-observed-major-change", "--follow"];
|
|
60861
|
+
var PROJECTION_PUBLISH_STATUS_VALUE_FLAGS = ["--base", "--remote"];
|
|
60862
|
+
var PROJECTION_VERIFY_PR_VALUE_FLAGS = ["--base"];
|
|
60863
|
+
var PROJECTION_PUBLISH_DEFAULT_INTERVAL_SECONDS = 300;
|
|
60864
|
+
var PROJECTION_PUBLISH_MIN_INTERVAL_SECONDS = 30;
|
|
59614
60865
|
var CLI_COMMAND_FLAGS = {
|
|
59615
60866
|
debt: { value: ["--changed-path"], boolean: [] },
|
|
59616
60867
|
"agent-context": { value: ["--expected-worktree-digest", "--id", "--task-session-id"], boolean: ["--approved"] },
|
|
@@ -59724,7 +60975,10 @@ var CLI_COMMAND_FLAGS = {
|
|
|
59724
60975
|
recover: { value: ["--request-json"], boolean: [] },
|
|
59725
60976
|
readback: { value: ["--request-json"], boolean: [] },
|
|
59726
60977
|
snapshot: { value: [], boolean: [] },
|
|
59727
|
-
freshness: { value: ["--changed-path"], boolean: [] }
|
|
60978
|
+
freshness: { value: ["--changed-path"], boolean: [] },
|
|
60979
|
+
publish: { value: PROJECTION_PUBLISH_VALUE_FLAGS, boolean: PROJECTION_PUBLISH_BOOLEAN_FLAGS },
|
|
60980
|
+
"publish-status": { value: PROJECTION_PUBLISH_STATUS_VALUE_FLAGS, boolean: [] },
|
|
60981
|
+
"verify-pr": { value: PROJECTION_VERIFY_PR_VALUE_FLAGS, boolean: [] }
|
|
59728
60982
|
}
|
|
59729
60983
|
},
|
|
59730
60984
|
recommendations: {
|
|
@@ -59859,6 +61113,48 @@ var CLI_PROJECTION_FRESHNESS_HELP = {
|
|
|
59859
61113
|
writes: "none; no daemon, no local store, no CodeGraph sync. Outside a Git worktree it fails with AC_REPO_NOT_FOUND (git-worktree-required).",
|
|
59860
61114
|
capability: "projection-freshness-v1"
|
|
59861
61115
|
};
|
|
61116
|
+
var CLI_PROJECTION_PUBLISH_HELP = {
|
|
61117
|
+
usage: "archctx projection publish [--base <branch>] [--remote <name>] [--branch <archctx/...>] [--publisher gh|command|none] [--publisher-command <executable>] [--accept-observed-major-change] [--follow [--interval <seconds>]] [--json]",
|
|
61118
|
+
description: "Projection writer mode. Fetches the remote base tip, resets a writer-owned worktree to it, applies the projection through that worktree's archctxd, and pushes the result as exactly one commit on top of the base tip to a rolling archctx/* branch, then opens or updates its pull request. Only paths the apply wrote that are projection-owned outputs may change; anything else fails before a commit. One run per remote and base at a time (busy otherwise); the same base SHA is a no-op once published or up to date; a base that moves during the run is re-evaluated and the stale commit is never pushed.",
|
|
61119
|
+
options: {
|
|
61120
|
+
"--base <branch>": "The branch the projection is computed for (default: main).",
|
|
61121
|
+
"--remote <name>": "The remote to fetch the base from and push the rolling branch to (default: origin).",
|
|
61122
|
+
"--branch <archctx/...>": "The rolling branch (default: archctx/projection). It must start with archctx/ and differ from --base; the writer never pushes to any other ref.",
|
|
61123
|
+
"--publisher gh|command|none": "gh (default): the operator's own gh lists, then edits or creates the pull request. command: run --publisher-command. none: push only.",
|
|
61124
|
+
"--publisher-command <executable>": "With --publisher command: an executable run with no shell or arguments in the owned worktree, with PATH, HOME and ARCHCTX_PUBLISH_REMOTE/BASE/BRANCH/BASE_SHA/COMMIT/TITLE/BODY_FILE only (on Windows also SystemRoot, windir, ComSpec, PATHEXT, TEMP, TMP, USERPROFILE; #! scripts run through their interpreter on PATH; .cmd/.bat are rejected). Optional stdout: a JSON object {url?, number?}.",
|
|
61125
|
+
"--accept-observed-major-change": "Accept the major change the run observes (ProjectionRequestV1.acceptObservedMajorChange). Without it a major change stops at human-action-required and nothing is pushed.",
|
|
61126
|
+
"--follow": "Run as a foreground loop: one publish per --interval, one JSON envelope line per run on stdout, until SIGINT or SIGTERM. For launchd/systemd KeepAlive on an always-on host.",
|
|
61127
|
+
"--interval <seconds>": `With --follow: seconds between runs (default ${PROJECTION_PUBLISH_DEFAULT_INTERVAL_SECONDS}, minimum ${PROJECTION_PUBLISH_MIN_INTERVAL_SECONDS}).`,
|
|
61128
|
+
"--json": "Accepted for symmetry; output is always JSON.",
|
|
61129
|
+
"--help, -h": "Show help without fetching, pushing or starting a daemon."
|
|
61130
|
+
},
|
|
61131
|
+
results: "published, up-to-date, already-processed, human-action-required, busy (ok) or failed (ok: false with the run as data).",
|
|
61132
|
+
writes: "the writer state, lock and owned worktree under the repository runtime state directory; one commit on the owned worktree; a push to refs/heads/<--branch> only. Outbound: the projection-publishing egress channel (git fetch/ls-remote/push, gh, the publisher command); ARCHCONTEXT_EGRESS_MODE=local-only refuses it with AC_POLICY_VIOLATION. archctx stores no credentials. It never runs codegraph init; when code facts are required, run codegraph init <worktreePath> once (see projection publish-status).",
|
|
61133
|
+
capability: "projection-publish-v1"
|
|
61134
|
+
};
|
|
61135
|
+
var CLI_PROJECTION_PUBLISH_STATUS_HELP = {
|
|
61136
|
+
usage: "archctx projection publish-status [--base <branch>] [--remote <name>] [--json]",
|
|
61137
|
+
description: "Read-only state of the projection writer for one remote and base: the owned worktree path, a running writer's pid, the last processed and last successful base SHAs and the last run.",
|
|
61138
|
+
options: {
|
|
61139
|
+
"--base <branch>": "Default: main.",
|
|
61140
|
+
"--remote <name>": "Default: origin.",
|
|
61141
|
+
"--json": "Accepted for symmetry; output is always JSON.",
|
|
61142
|
+
"--help, -h": "Show help without reading anything."
|
|
61143
|
+
},
|
|
61144
|
+
writes: "none; no fetch, no daemon.",
|
|
61145
|
+
capability: "projection-publish-v1"
|
|
61146
|
+
};
|
|
61147
|
+
var CLI_PROJECTION_VERIFY_PR_HELP = {
|
|
61148
|
+
usage: "archctx projection verify-pr --base <ref> [--json]",
|
|
61149
|
+
description: "Checks the checked-out pull request head (HEAD, clean tracked worktree) against --base. A changed path is generated only when it is the projection manifest, a manifest target path or an agent-context target path at HEAD or at the merge base, and, for a mixed-ownership target, its text outside the generated regions is unchanged (otherwise it is listed in humanEditedPaths). Only a generated-only diff is rendered (projection check, plus the agent-context render for changed contract files). verdict is no-changes, touches-non-generated (answered from the diff alone, nothing rendered), generated-only-render-equivalent (generated files and regions equal a fresh render and no text outside them changed) or generated-only-not-render-equivalent. A mixed target that is added or deleted, or whose markers do not parse, is conservatively a human edit. The envelope is ok for every valid evaluation; the verdict carries the decision.",
|
|
61150
|
+
options: {
|
|
61151
|
+
"--base <ref>": "Required. The pull request base, for example origin/main.",
|
|
61152
|
+
"--json": "Accepted for symmetry; output is always JSON.",
|
|
61153
|
+
"--help, -h": "Show help without reading the repository."
|
|
61154
|
+
},
|
|
61155
|
+
writes: "none; no daemon, no local store. A dirty tracked worktree fails with AC_PRECONDITION_FAILED. A generated-only diff renders in-process like projection check, so it needs code facts when the manifest requires them.",
|
|
61156
|
+
capability: "projection-verify-pr-v1"
|
|
61157
|
+
};
|
|
59862
61158
|
function refactorFlagValue(args, flag) {
|
|
59863
61159
|
const index = args.indexOf(flag);
|
|
59864
61160
|
if (index === -1)
|
|
@@ -59995,14 +61291,14 @@ async function runCliUnchecked(command = "help", args = [], cwd, deps = {}) {
|
|
|
59995
61291
|
case "status":
|
|
59996
61292
|
return (await runtime()).runtimeStatus(cwd);
|
|
59997
61293
|
case "projection":
|
|
59998
|
-
return await runProjectionProtocolCommand2(args, cwd, runtime);
|
|
61294
|
+
return await runProjectionProtocolCommand2(args, cwd, runtime, deps);
|
|
59999
61295
|
case "flows":
|
|
60000
61296
|
return runFlowsCommand(args, cwd);
|
|
60001
61297
|
case "repo": {
|
|
60002
61298
|
const subcommand = args[0] ?? "list";
|
|
60003
61299
|
if (subcommand === "add") {
|
|
60004
61300
|
const root = readFlag(args, "--root") ?? cwd;
|
|
60005
|
-
if (
|
|
61301
|
+
if (resolve35(root) !== resolve35(cwd)) {
|
|
60006
61302
|
return errorEnvelope("repo.add", "AC_CAPABILITY_UNSUPPORTED", "Multi-repo architecture context is outside the Local Core MVP; run archctx inside one Git repository.");
|
|
60007
61303
|
}
|
|
60008
61304
|
const daemon = await runtime();
|
|
@@ -60244,7 +61540,7 @@ async function runCliUnchecked(command = "help", args = [], cwd, deps = {}) {
|
|
|
60244
61540
|
const read = await readResolvePathsFrom(args, cwd, deps);
|
|
60245
61541
|
if ("problem" in read)
|
|
60246
61542
|
return errorEnvelope("resolve", "AC_SCHEMA_INVALID", read.problem);
|
|
60247
|
-
const model =
|
|
61543
|
+
const model = loadResolveModel(cwd);
|
|
60248
61544
|
const outcomes = resolveArchitectureOwnersForPaths(model.nodes, read.paths);
|
|
60249
61545
|
return okEnvelope("resolve", { results: outcomes.map((outcome, index) => resolveResultData(read.paths[index], outcome)) });
|
|
60250
61546
|
}
|
|
@@ -60254,7 +61550,7 @@ async function runCliUnchecked(command = "help", args = [], cwd, deps = {}) {
|
|
|
60254
61550
|
if (!isRepoRelativePosixPath(path)) {
|
|
60255
61551
|
return errorEnvelope("resolve", "AC_SCHEMA_INVALID", "resolve --path must be a repository-relative POSIX path");
|
|
60256
61552
|
}
|
|
60257
|
-
const model =
|
|
61553
|
+
const model = loadResolveModel(cwd);
|
|
60258
61554
|
return okEnvelope("resolve", resolveResultData(path, resolveArchitectureOwnerForPath(model.nodes, path)));
|
|
60259
61555
|
}
|
|
60260
61556
|
case "help":
|
|
@@ -60346,6 +61642,12 @@ function cliHelpEnvelope(command, args) {
|
|
|
60346
61642
|
case "projection":
|
|
60347
61643
|
if (subcommand === "freshness")
|
|
60348
61644
|
return okEnvelope("help", CLI_PROJECTION_FRESHNESS_HELP);
|
|
61645
|
+
if (subcommand === "publish")
|
|
61646
|
+
return okEnvelope("help", CLI_PROJECTION_PUBLISH_HELP);
|
|
61647
|
+
if (subcommand === "publish-status")
|
|
61648
|
+
return okEnvelope("help", CLI_PROJECTION_PUBLISH_STATUS_HELP);
|
|
61649
|
+
if (subcommand === "verify-pr")
|
|
61650
|
+
return okEnvelope("help", CLI_PROJECTION_VERIFY_PR_HELP);
|
|
60349
61651
|
break;
|
|
60350
61652
|
case "daemon":
|
|
60351
61653
|
return okEnvelope("daemon", {
|
|
@@ -61062,8 +62364,10 @@ async function runAgentContextProjectionCommand2(args, cwd, daemon) {
|
|
|
61062
62364
|
expectedWorktreeDigest: readFlag(args, "--expected-worktree-digest")
|
|
61063
62365
|
});
|
|
61064
62366
|
}
|
|
61065
|
-
async function runProjectionProtocolCommand2(args, cwd, runtime) {
|
|
62367
|
+
async function runProjectionProtocolCommand2(args, cwd, runtime, deps) {
|
|
61066
62368
|
const action = args[0] ?? "run";
|
|
62369
|
+
if (action === "publish" || action === "publish-status" || action === "verify-pr")
|
|
62370
|
+
return runProjectionWriterCliCommand(action, args.slice(1), cwd, deps);
|
|
61067
62371
|
if (action === "snapshot") {
|
|
61068
62372
|
const unexpected = args.slice(1).filter((arg) => arg !== "--json");
|
|
61069
62373
|
if (unexpected.length > 0)
|
|
@@ -61073,7 +62377,7 @@ async function runProjectionProtocolCommand2(args, cwd, runtime) {
|
|
|
61073
62377
|
if (action === "freshness")
|
|
61074
62378
|
return runProjectionFreshnessCliCommand(args.slice(1), cwd);
|
|
61075
62379
|
if (!["run", "recover", "readback"].includes(action))
|
|
61076
|
-
return errorEnvelope("projection", "AC_SCHEMA_INVALID", "projection requires run|recover|readback --request-json <request>,
|
|
62380
|
+
return errorEnvelope("projection", "AC_SCHEMA_INVALID", "projection requires run|recover|readback --request-json <request>, snapshot|freshness [--json], or publish|publish-status|verify-pr");
|
|
61077
62381
|
const raw = readFlag(args, "--request-json");
|
|
61078
62382
|
if (!raw)
|
|
61079
62383
|
return errorEnvelope(`projection.${action}`, "AC_SCHEMA_INVALID", `projection ${action} requires --request-json`);
|
|
@@ -61110,6 +62414,145 @@ function runProjectionFreshnessCliCommand(args, cwd) {
|
|
|
61110
62414
|
}
|
|
61111
62415
|
return runProjectionFreshnessCommand(cwd, changedPaths.length > 0 ? { changedPaths } : {});
|
|
61112
62416
|
}
|
|
62417
|
+
async function runProjectionWriterCliCommand(action, args, cwd, deps) {
|
|
62418
|
+
const requestId = `projection.${action}`;
|
|
62419
|
+
const valueFlags = action === "publish" ? PROJECTION_PUBLISH_VALUE_FLAGS : action === "publish-status" ? PROJECTION_PUBLISH_STATUS_VALUE_FLAGS : PROJECTION_VERIFY_PR_VALUE_FLAGS;
|
|
62420
|
+
const stray = projectionWriterStrayToken(args, valueFlags);
|
|
62421
|
+
if (stray !== undefined)
|
|
62422
|
+
return errorEnvelope(requestId, "AC_SCHEMA_INVALID", `projection ${action} does not accept ${stray}; use --help`, "cli-input-invalid");
|
|
62423
|
+
if (action === "verify-pr") {
|
|
62424
|
+
const base = readFlag(args, "--base");
|
|
62425
|
+
if (!base)
|
|
62426
|
+
return errorEnvelope(requestId, "AC_SCHEMA_INVALID", "projection verify-pr requires --base <ref>", "cli-input-invalid");
|
|
62427
|
+
return runProjectionVerifyPrCommand({ cwd, base });
|
|
62428
|
+
}
|
|
62429
|
+
if (action === "publish-status") {
|
|
62430
|
+
return runProjectionPublishStatusCommand({ cwd, base: readFlag(args, "--base"), remote: readFlag(args, "--remote") });
|
|
62431
|
+
}
|
|
62432
|
+
const parsed = parseProjectionPublishCliInput(args, cwd);
|
|
62433
|
+
if (!parsed.ok)
|
|
62434
|
+
return parsed.envelope;
|
|
62435
|
+
if (parsed.input.follow) {
|
|
62436
|
+
return errorEnvelope(requestId, "AC_SCHEMA_INVALID", "projection publish --follow is a foreground process loop of the archctx executable; without --follow this command runs once", "cli-input-invalid");
|
|
62437
|
+
}
|
|
62438
|
+
return runProjectionPublishOnce(parsed.input, cwd, deps);
|
|
62439
|
+
}
|
|
62440
|
+
function projectionWriterStrayToken(args, valueFlags) {
|
|
62441
|
+
for (let index = 0;index < args.length; index += 1) {
|
|
62442
|
+
const token = args[index];
|
|
62443
|
+
if (valueFlags.includes(token) || token === "--format") {
|
|
62444
|
+
index += 1;
|
|
62445
|
+
continue;
|
|
62446
|
+
}
|
|
62447
|
+
if (isCliFlagToken(token))
|
|
62448
|
+
continue;
|
|
62449
|
+
return token;
|
|
62450
|
+
}
|
|
62451
|
+
return;
|
|
62452
|
+
}
|
|
62453
|
+
function parseProjectionPublishCliInput(args, cwd) {
|
|
62454
|
+
const invalid = (message) => ({ ok: false, envelope: errorEnvelope("projection.publish", "AC_SCHEMA_INVALID", message, "cli-input-invalid") });
|
|
62455
|
+
const publisherKind = readFlag(args, "--publisher") ?? "gh";
|
|
62456
|
+
const publisherCommand = readFlag(args, "--publisher-command");
|
|
62457
|
+
const follow = args.includes("--follow");
|
|
62458
|
+
const intervalFlag = readFlag(args, "--interval");
|
|
62459
|
+
if (intervalFlag !== undefined && !follow)
|
|
62460
|
+
return invalid("projection publish --interval requires --follow");
|
|
62461
|
+
const intervalSeconds = intervalFlag === undefined ? PROJECTION_PUBLISH_DEFAULT_INTERVAL_SECONDS : Number(intervalFlag);
|
|
62462
|
+
if (!/^\d+$/.test(intervalFlag ?? "0") || !Number.isSafeInteger(intervalSeconds) || intervalSeconds < PROJECTION_PUBLISH_MIN_INTERVAL_SECONDS) {
|
|
62463
|
+
return invalid(`projection publish --interval must be an integer number of seconds, at least ${PROJECTION_PUBLISH_MIN_INTERVAL_SECONDS}`);
|
|
62464
|
+
}
|
|
62465
|
+
let publisher;
|
|
62466
|
+
if (publisherKind === "gh" || publisherKind === "none") {
|
|
62467
|
+
if (publisherCommand !== undefined)
|
|
62468
|
+
return invalid("projection publish --publisher-command requires --publisher command");
|
|
62469
|
+
publisher = publisherKind === "gh" ? createGhProjectionPublisher() : createNoneProjectionPublisher();
|
|
62470
|
+
} else if (publisherKind === "command") {
|
|
62471
|
+
if (!publisherCommand)
|
|
62472
|
+
return invalid("projection publish --publisher command requires --publisher-command <executable>");
|
|
62473
|
+
const command = resolve35(cwd, publisherCommand);
|
|
62474
|
+
if (!existsSync35(command) || !statSync11(command).isFile())
|
|
62475
|
+
return invalid(`projection publish --publisher-command is not a file: ${command}`);
|
|
62476
|
+
publisher = createCommandProjectionPublisher({ command });
|
|
62477
|
+
} else {
|
|
62478
|
+
return invalid("projection publish --publisher must be gh, command or none");
|
|
62479
|
+
}
|
|
62480
|
+
return {
|
|
62481
|
+
ok: true,
|
|
62482
|
+
input: {
|
|
62483
|
+
remote: readFlag(args, "--remote"),
|
|
62484
|
+
base: readFlag(args, "--base"),
|
|
62485
|
+
branch: readFlag(args, "--branch"),
|
|
62486
|
+
publisher,
|
|
62487
|
+
acceptObservedMajorChange: args.includes("--accept-observed-major-change"),
|
|
62488
|
+
follow,
|
|
62489
|
+
intervalSeconds
|
|
62490
|
+
}
|
|
62491
|
+
};
|
|
62492
|
+
}
|
|
62493
|
+
function runProjectionPublishOnce(input, cwd, deps) {
|
|
62494
|
+
const clients = new Map;
|
|
62495
|
+
return runProjectionPublishCommand({
|
|
62496
|
+
cwd,
|
|
62497
|
+
remote: input.remote,
|
|
62498
|
+
base: input.base,
|
|
62499
|
+
branch: input.branch,
|
|
62500
|
+
publisher: input.publisher,
|
|
62501
|
+
acceptObservedMajorChange: input.acceptObservedMajorChange,
|
|
62502
|
+
apply: async (worktree, request) => {
|
|
62503
|
+
let client = deps.runtimeClient ?? clients.get(worktree);
|
|
62504
|
+
if (!client) {
|
|
62505
|
+
client = await createOrStartRuntimeRpcClient(worktree);
|
|
62506
|
+
clients.set(worktree, client);
|
|
62507
|
+
}
|
|
62508
|
+
return client.projection(worktree, { action: "run", request });
|
|
62509
|
+
}
|
|
62510
|
+
});
|
|
62511
|
+
}
|
|
62512
|
+
async function runProjectionPublishFollowEntry(args, cwd) {
|
|
62513
|
+
const stray = projectionWriterStrayToken(args, PROJECTION_PUBLISH_VALUE_FLAGS);
|
|
62514
|
+
const parsed = stray === undefined ? parseProjectionPublishCliInput(args, cwd) : { ok: false, envelope: errorEnvelope("projection.publish", "AC_SCHEMA_INVALID", `projection publish does not accept ${stray}; use --help`, "cli-input-invalid") };
|
|
62515
|
+
if (!parsed.ok) {
|
|
62516
|
+
process.stdout.write(`${JSON.stringify(parsed.envelope)}
|
|
62517
|
+
`);
|
|
62518
|
+
return 1;
|
|
62519
|
+
}
|
|
62520
|
+
const controller = new AbortController;
|
|
62521
|
+
const stop = () => controller.abort();
|
|
62522
|
+
process.on("SIGINT", stop);
|
|
62523
|
+
process.on("SIGTERM", stop);
|
|
62524
|
+
try {
|
|
62525
|
+
await runProjectionPublishFollow({
|
|
62526
|
+
runOnce: () => runProjectionPublishOnce(parsed.input, cwd, {}),
|
|
62527
|
+
intervalMs: parsed.input.intervalSeconds * 1000,
|
|
62528
|
+
sleep: (ms) => sleepUnlessAborted(ms, controller.signal),
|
|
62529
|
+
shouldStop: () => controller.signal.aborted,
|
|
62530
|
+
onResult: (envelope) => {
|
|
62531
|
+
process.stdout.write(`${JSON.stringify(envelope)}
|
|
62532
|
+
`);
|
|
62533
|
+
}
|
|
62534
|
+
});
|
|
62535
|
+
} finally {
|
|
62536
|
+
process.off("SIGINT", stop);
|
|
62537
|
+
process.off("SIGTERM", stop);
|
|
62538
|
+
}
|
|
62539
|
+
return 0;
|
|
62540
|
+
}
|
|
62541
|
+
function sleepUnlessAborted(ms, signal) {
|
|
62542
|
+
return new Promise((resolvePromise) => {
|
|
62543
|
+
if (signal.aborted) {
|
|
62544
|
+
resolvePromise();
|
|
62545
|
+
return;
|
|
62546
|
+
}
|
|
62547
|
+
const timer = setTimeout(done, ms);
|
|
62548
|
+
function done() {
|
|
62549
|
+
clearTimeout(timer);
|
|
62550
|
+
signal.removeEventListener("abort", done);
|
|
62551
|
+
resolvePromise();
|
|
62552
|
+
}
|
|
62553
|
+
signal.addEventListener("abort", done, { once: true });
|
|
62554
|
+
});
|
|
62555
|
+
}
|
|
61113
62556
|
function runFlowsCommand(args, cwd) {
|
|
61114
62557
|
const formatAt = args.indexOf("--format");
|
|
61115
62558
|
const unexpected = args.slice(1).filter((arg, index) => arg !== "--json" && (formatAt < 0 || index + 1 !== formatAt && index !== formatAt));
|
|
@@ -61473,12 +62916,12 @@ function auditManifestGateRoot(cwd) {
|
|
|
61473
62916
|
}
|
|
61474
62917
|
}
|
|
61475
62918
|
function auditGithubIssuesEnabled(cwd) {
|
|
61476
|
-
const manifestPath =
|
|
61477
|
-
if (!
|
|
62919
|
+
const manifestPath = resolve35(auditManifestGateRoot(cwd), ".archcontext/manifest.yaml");
|
|
62920
|
+
if (!existsSync35(manifestPath))
|
|
61478
62921
|
return false;
|
|
61479
62922
|
let raw;
|
|
61480
62923
|
try {
|
|
61481
|
-
raw =
|
|
62924
|
+
raw = readFileSync32(manifestPath, "utf8");
|
|
61482
62925
|
} catch {
|
|
61483
62926
|
return false;
|
|
61484
62927
|
}
|
|
@@ -61763,8 +63206,8 @@ async function runGithubCommand(args, cwd, deps) {
|
|
|
61763
63206
|
}
|
|
61764
63207
|
if (subcommand === "disconnect") {
|
|
61765
63208
|
const connection = await deps.githubConnectionReader?.readVerifiedConnection({ repositoryRoot: cwd });
|
|
61766
|
-
const localRecordRemoved =
|
|
61767
|
-
|
|
63209
|
+
const localRecordRemoved = existsSync35(connectionPath);
|
|
63210
|
+
rmSync16(connectionPath, { force: true });
|
|
61768
63211
|
return okEnvelope("github.disconnect", {
|
|
61769
63212
|
connected: connection !== undefined,
|
|
61770
63213
|
localRecordRemoved,
|
|
@@ -62051,7 +63494,7 @@ async function digestReviewChallenge(challenge) {
|
|
|
62051
63494
|
return reviewChallengeV2Digest(challenge);
|
|
62052
63495
|
}
|
|
62053
63496
|
function defaultGithubConnectionPath(cwd) {
|
|
62054
|
-
return
|
|
63497
|
+
return join27(dirname16(defaultDaemonConnectionPath(cwd)), "github-connection.json");
|
|
62055
63498
|
}
|
|
62056
63499
|
function sanitizeGithubConnection(record, connectionPath) {
|
|
62057
63500
|
return {
|
|
@@ -62075,7 +63518,7 @@ async function readReviewChallengeV2Arg(args, cwd, commandName = "github verify-
|
|
|
62075
63518
|
if (!inline && !challengePath)
|
|
62076
63519
|
return { ok: false, message: `${commandName} requires --challenge-json or --challenge-path` };
|
|
62077
63520
|
try {
|
|
62078
|
-
const raw = inline ??
|
|
63521
|
+
const raw = inline ?? readFileSync32(resolve35(cwd, challengePath), "utf8");
|
|
62079
63522
|
const parsed = JSON.parse(raw);
|
|
62080
63523
|
const attestation = await Promise.resolve().then(() => (init_src6(), {}));
|
|
62081
63524
|
const assertReviewChallengeV2 = assertReviewChallengeV22;
|
|
@@ -62459,7 +63902,7 @@ function readLatestPackageVersion(env) {
|
|
|
62459
63902
|
if (env[LATEST_VERSION_ENV]) {
|
|
62460
63903
|
return { source: "env", version: env[LATEST_VERSION_ENV] };
|
|
62461
63904
|
}
|
|
62462
|
-
const result = withLocalEgress("npm-update-check", () =>
|
|
63905
|
+
const result = withLocalEgress("npm-update-check", () => spawnSync6("npm", ["view", RELEASE_PACKAGE_NAME, "version", "--json"], {
|
|
62463
63906
|
encoding: "utf8",
|
|
62464
63907
|
timeout: NPM_VIEW_TIMEOUT_MS,
|
|
62465
63908
|
shell: false
|
|
@@ -62553,10 +63996,10 @@ function doctorSqlite(cwd) {
|
|
|
62553
63996
|
const legacyLocalStore = inspectLegacyLocalStoreMigration(cwd);
|
|
62554
63997
|
return {
|
|
62555
63998
|
path,
|
|
62556
|
-
exists:
|
|
63999
|
+
exists: existsSync35(path),
|
|
62557
64000
|
migrations: productVersionManifest().runtime.sqliteMigrations,
|
|
62558
64001
|
legacyPath: paths.legacyLocalStorePath,
|
|
62559
|
-
legacyExists:
|
|
64002
|
+
legacyExists: existsSync35(paths.legacyLocalStorePath),
|
|
62560
64003
|
legacyLocalStore
|
|
62561
64004
|
};
|
|
62562
64005
|
}
|
|
@@ -62577,8 +64020,8 @@ function runtimePathsReport(cwd) {
|
|
|
62577
64020
|
...paths,
|
|
62578
64021
|
legacyLocalStore: inspectLegacyLocalStoreMigration(cwd),
|
|
62579
64022
|
runtimeRepositoryId: repositoryFingerprint2(paths.repositoryRoot),
|
|
62580
|
-
repositoryTruthDir:
|
|
62581
|
-
codeGraphIndexDir:
|
|
64023
|
+
repositoryTruthDir: join27(paths.repositoryRoot, ".archcontext"),
|
|
64024
|
+
codeGraphIndexDir: join27(paths.repositoryRoot, ".codegraph"),
|
|
62582
64025
|
npmGlobalInstallState: "forbidden",
|
|
62583
64026
|
overrides: {
|
|
62584
64027
|
stateRootEnv: "ARCHCONTEXT_STATE_DIR",
|
|
@@ -62587,7 +64030,7 @@ function runtimePathsReport(cwd) {
|
|
|
62587
64030
|
};
|
|
62588
64031
|
}
|
|
62589
64032
|
function pathAccess(path) {
|
|
62590
|
-
const exists =
|
|
64033
|
+
const exists = existsSync35(path);
|
|
62591
64034
|
return {
|
|
62592
64035
|
path,
|
|
62593
64036
|
exists,
|
|
@@ -62828,7 +64271,7 @@ function daemonStopStateDir(args, cwd) {
|
|
|
62828
64271
|
return { ok: false, envelope: errorEnvelope("daemon.stop", "AC_SCHEMA_INVALID", "daemon stop accepts at most one of --state-dir or --workspace-id") };
|
|
62829
64272
|
}
|
|
62830
64273
|
if (stateDir !== undefined)
|
|
62831
|
-
return { ok: true, stateDir:
|
|
64274
|
+
return { ok: true, stateDir: resolve35(cwd, stateDir) };
|
|
62832
64275
|
if (workspaceId === undefined)
|
|
62833
64276
|
return { ok: true };
|
|
62834
64277
|
if (!/^ws\.[0-9a-f]{16}$/.test(workspaceId)) {
|
|
@@ -62864,9 +64307,9 @@ async function startBackgroundDaemon(args, cwd) {
|
|
|
62864
64307
|
const recovery = discovered.recovery;
|
|
62865
64308
|
const connectionPath = defaultDaemonConnectionPath(cwd);
|
|
62866
64309
|
const controlDir = dirname16(connectionPath);
|
|
62867
|
-
const logPath =
|
|
62868
|
-
|
|
62869
|
-
const logFd =
|
|
64310
|
+
const logPath = join27(controlDir, "archctxd.log");
|
|
64311
|
+
mkdirSync14(controlDir, { recursive: true });
|
|
64312
|
+
const logFd = openSync13(logPath, "a", 384);
|
|
62870
64313
|
try {
|
|
62871
64314
|
let childExit;
|
|
62872
64315
|
let childError;
|
|
@@ -62906,7 +64349,7 @@ async function startBackgroundDaemon(args, cwd) {
|
|
|
62906
64349
|
...recoveryData(recovery)
|
|
62907
64350
|
});
|
|
62908
64351
|
} finally {
|
|
62909
|
-
|
|
64352
|
+
closeSync13(logFd);
|
|
62910
64353
|
}
|
|
62911
64354
|
}
|
|
62912
64355
|
async function upgradeDaemon(args, cwd) {
|
|
@@ -62915,7 +64358,7 @@ async function upgradeDaemon(args, cwd) {
|
|
|
62915
64358
|
const record = readRuntimeRpcConnectionFile(cwd);
|
|
62916
64359
|
const unchanged = () => JSON.stringify(readRuntimeRpcConnectionFile(cwd)) === JSON.stringify(record);
|
|
62917
64360
|
const refuse = (reason) => errorEnvelope("daemon.upgrade", "AC_RUNTIME_VERSION_UNSUPPORTED", `Daemon ownership is unproven (${reason}); no process was signalled and no replacement was started. Run the previous archctx daemon stop, or verify and stop the original daemon in its owning terminal, then run archctx daemon upgrade.`, "daemon-ownership-unproven");
|
|
62918
|
-
if (
|
|
64361
|
+
if (existsSync35(connectionPath) && (!record || typeof record.pid !== "number" || !Number.isInteger(record.pid) || record.pid <= 0)) {
|
|
62919
64362
|
return refuse("connection record cannot prove a daemon identity");
|
|
62920
64363
|
}
|
|
62921
64364
|
let lockOwner;
|
|
@@ -62965,9 +64408,9 @@ async function upgradeDaemon(args, cwd) {
|
|
|
62965
64408
|
if (!stopped.ok)
|
|
62966
64409
|
return refuse("live instance refused the expected identity");
|
|
62967
64410
|
const deadline = Date.now() + daemonStartTimeoutMs(args);
|
|
62968
|
-
while (
|
|
64411
|
+
while (existsSync35(connectionPath) || existsSync35(lockPath)) {
|
|
62969
64412
|
const current = readRuntimeRpcConnectionFile(cwd);
|
|
62970
|
-
if (current ? JSON.stringify(current) !== JSON.stringify(record) :
|
|
64413
|
+
if (current ? JSON.stringify(current) !== JSON.stringify(record) : existsSync35(connectionPath)) {
|
|
62971
64414
|
return refuse("connection changed or cannot be verified during self-stop");
|
|
62972
64415
|
}
|
|
62973
64416
|
const owner = liveDaemonLockOwnerPid(cwd);
|
|
@@ -62987,7 +64430,7 @@ async function upgradeDaemon(args, cwd) {
|
|
|
62987
64430
|
const started = await startBackgroundDaemon(args, cwd);
|
|
62988
64431
|
return started.ok ? { ...started, requestId: "daemon.upgrade", data: { ...started.data, upgraded: false, reason: "runtime-compatible" } } : started;
|
|
62989
64432
|
}
|
|
62990
|
-
if (selfStopped ?
|
|
64433
|
+
if (selfStopped ? existsSync35(connectionPath) || existsSync35(lockPath) : !unchanged() || record?.pid !== undefined && isPidAlive(record.pid) || liveDaemonLockOwnerPid(cwd) !== undefined) {
|
|
62991
64434
|
return refuse("ownership changed before replacement");
|
|
62992
64435
|
}
|
|
62993
64436
|
const recovery = recoverStaleDaemonControlFiles(cwd, { removeUnhealthyConnection: true });
|
|
@@ -63153,7 +64596,7 @@ function daemonStartTimeoutMs(args) {
|
|
|
63153
64596
|
}
|
|
63154
64597
|
function daemonStartFailureMessage(cwd, logPath, childPid, childExit, childError) {
|
|
63155
64598
|
const childState = childError ? `childError=${childError.message}` : childExit ? `childExit=${childExit.code ?? "null"}/${childExit.signal ?? "none"}` : "childState=still-running";
|
|
63156
|
-
const lockOwnerPid =
|
|
64599
|
+
const lockOwnerPid = existsSync35(defaultDaemonConnectionPath(cwd)) ? undefined : liveDaemonLockOwnerPid(cwd);
|
|
63157
64600
|
const owner = lockOwnerPid === undefined || lockOwnerPid === childPid ? "" : `; ${orphanedDaemonMessage(lockOwnerPid)}`;
|
|
63158
64601
|
return `archctxd did not become ready; ${childState}${owner}; log=${logPath}; logTail=${readFileTail(logPath)}`;
|
|
63159
64602
|
}
|
|
@@ -63174,7 +64617,7 @@ function sleep2(ms) {
|
|
|
63174
64617
|
}
|
|
63175
64618
|
function readFileTail(path, maxBytes = 4096) {
|
|
63176
64619
|
try {
|
|
63177
|
-
const content =
|
|
64620
|
+
const content = readFileSync32(path, "utf8");
|
|
63178
64621
|
return content.slice(Math.max(0, content.length - maxBytes)).replace(/\s+/g, " ").trim();
|
|
63179
64622
|
} catch {
|
|
63180
64623
|
return "<unavailable>";
|
|
@@ -63259,7 +64702,7 @@ function resolveCommandExitCode(result) {
|
|
|
63259
64702
|
return 2;
|
|
63260
64703
|
return 1;
|
|
63261
64704
|
}
|
|
63262
|
-
var CLI_HELP_EXAMPLES = ["archctx init --name MyApp", "archctx projection run --request-json '{...}'", "archctx projection snapshot --json", "archctx projection freshness --json", "archctx projection freshness --changed-path src/app.ts --json", "archctx plan --id changeset.<id> --op update_entity_fields --path .archcontext/model/nodes/<node>.yaml --expected-hash sha256:<64-hex> --body '<complete YAML>'", "archctx plan --id changeset.<id> --op delete_entity --path .archcontext/model/flows/<flow>.yaml --expected-hash sha256:<64-hex>", "archctx hash --path .archcontext/model/nodes/<node>.yaml", "archctx plan --id changeset.<id> --operations-file <operations.json>", "archctx plan --id changeset.<id> --op update_entity_fields --path .archcontext/model/nodes/<node>.yaml --expected-hash sha256:<64-hex> --body-file <entity.yaml>", "archctx apply --id changeset.<id> --approved --expected-worktree-digest <draft.base.worktreeDigest>", "archctx state recover --from-git", "archctx ledger migrate --from-yaml --dry-run", "archctx ledger promote --mode authoritative --preflight --rollback-plan", "archctx book recommendations --open --explain", "archctx recommendations accept --id recommendation.<id> --reason 'Accepted after local readback.'", "archctx recommendations metrics", "archctx recommendations list --status rejected", "archctx refactor scan --json", "archctx refactor verify --request-json '{...}' --json", "archctx practices validate --strict", "archctx practices list --json", "archctx practices waivers", "archctx practices waive --practice-id modularity.no-new-cycle --owner team-architecture --reason 'External migration window requires this edge until cutover.' --review-at 2026-07-10T00:00:00.000Z --expires-at 2026-07-24T00:00:00.000Z --evidence-digest sha256:<64-hex> --subject module.a->module.b", "archctx checkpoint --changed-path src/app.ts", "archctx practices checkpoint --task-session-id task_cli", "archctx investigate --runner-port codex", "archctx agents status --status queued,running", "archctx agents budget", "archctx hook enqueue --event post-edit --path src/app.ts", "archctx jobs list --status queued", "archctx audit consent", "archctx audit consent --revoke", "archctx audit run --reason 'quarterly architecture audit'", "archctx audit run --no-wait", "archctx audit list --status pending", "archctx audit show audit_run.<id>", "archctx audit approve audit_run.<id>", "archctx audit approve audit_run.<id> --confirm-public-repo public:<host>/<owner>/<repo>:<baseSha>:<runId>", "archctx audit approve audit_run.<id> --resume", "archctx hooks install --host codex", "archctx paths", "archctx update --check", "archctx doctor --check-updates", "archctx github connect", "archctx github status", "archctx daemon start", "archctx explore start --foreground", "archctx docs anchors --json", "archctx export native --worktree --json", "archctx export native --ref HEAD --profile repo-harness/v1 --json", "archctx export likec4", "archctx import structurizr --content '<json>'", "archctx resolve --path packages/core/projection-engine/src/index.ts", "archctx tunnel", "archctx refactor scan --import-node-limit 100000 --json", "archctx resolve --paths-from changed-paths.txt", "archctx flows check --json", "archctx recommendations defer --id recommendation.<id> --import-node-limit 100000 --reason 'Scanned with the same limit.'", "archctx deps --json", "archctx deps --into capability.<id> --json"];
|
|
64705
|
+
var CLI_HELP_EXAMPLES = ["archctx init --name MyApp", "archctx projection run --request-json '{...}'", "archctx projection snapshot --json", "archctx projection freshness --json", "archctx projection freshness --changed-path src/app.ts --json", "archctx projection publish --json", "archctx projection publish --publisher none --json", "archctx projection publish --follow --interval 300", "archctx projection publish-status --json", "archctx projection verify-pr --base origin/main --json", "archctx plan --id changeset.<id> --op update_entity_fields --path .archcontext/model/nodes/<node>.yaml --expected-hash sha256:<64-hex> --body '<complete YAML>'", "archctx plan --id changeset.<id> --op delete_entity --path .archcontext/model/flows/<flow>.yaml --expected-hash sha256:<64-hex>", "archctx hash --path .archcontext/model/nodes/<node>.yaml", "archctx plan --id changeset.<id> --operations-file <operations.json>", "archctx plan --id changeset.<id> --op update_entity_fields --path .archcontext/model/nodes/<node>.yaml --expected-hash sha256:<64-hex> --body-file <entity.yaml>", "archctx apply --id changeset.<id> --approved --expected-worktree-digest <draft.base.worktreeDigest>", "archctx state recover --from-git", "archctx ledger migrate --from-yaml --dry-run", "archctx ledger promote --mode authoritative --preflight --rollback-plan", "archctx book recommendations --open --explain", "archctx recommendations accept --id recommendation.<id> --reason 'Accepted after local readback.'", "archctx recommendations metrics", "archctx recommendations list --status rejected", "archctx refactor scan --json", "archctx refactor verify --request-json '{...}' --json", "archctx practices validate --strict", "archctx practices list --json", "archctx practices waivers", "archctx practices waive --practice-id modularity.no-new-cycle --owner team-architecture --reason 'External migration window requires this edge until cutover.' --review-at 2026-07-10T00:00:00.000Z --expires-at 2026-07-24T00:00:00.000Z --evidence-digest sha256:<64-hex> --subject module.a->module.b", "archctx checkpoint --changed-path src/app.ts", "archctx practices checkpoint --task-session-id task_cli", "archctx investigate --runner-port codex", "archctx agents status --status queued,running", "archctx agents budget", "archctx hook enqueue --event post-edit --path src/app.ts", "archctx jobs list --status queued", "archctx audit consent", "archctx audit consent --revoke", "archctx audit run --reason 'quarterly architecture audit'", "archctx audit run --no-wait", "archctx audit list --status pending", "archctx audit show audit_run.<id>", "archctx audit approve audit_run.<id>", "archctx audit approve audit_run.<id> --confirm-public-repo public:<host>/<owner>/<repo>:<baseSha>:<runId>", "archctx audit approve audit_run.<id> --resume", "archctx hooks install --host codex", "archctx paths", "archctx update --check", "archctx doctor --check-updates", "archctx github connect", "archctx github status", "archctx daemon start", "archctx explore start --foreground", "archctx docs anchors --json", "archctx export native --worktree --json", "archctx export native --ref HEAD --profile repo-harness/v1 --json", "archctx export likec4", "archctx import structurizr --content '<json>'", "archctx resolve --path packages/core/projection-engine/src/index.ts", "archctx tunnel", "archctx refactor scan --import-node-limit 100000 --json", "archctx resolve --paths-from changed-paths.txt", "archctx flows check --json", "archctx recommendations defer --id recommendation.<id> --import-node-limit 100000 --reason 'Scanned with the same limit.'", "archctx deps --json", "archctx deps --into capability.<id> --json"];
|
|
63263
64706
|
var CLI_RESOLVE_HELP = {
|
|
63264
64707
|
command: "archctx resolve (--path <repo-relative path> | --paths-from <file> | --paths-from -)",
|
|
63265
64708
|
rule: "ADR-0043. source.exclude applies first. When every matching node lies on one parent chain, the deepest node owns the path; refactor scan uses the same rule. When matching nodes lie on different chains, an ancestor yields to its own descendant, then the longest literal source.include prefix wins, and an equal-length tie is ambiguous.",
|
|
@@ -63267,6 +64710,37 @@ var CLI_RESOLVE_HELP = {
|
|
|
63267
64710
|
result: "Each result carries path, matched, ambiguous and owningCapabilityId. A match adds stableId, kind, name, source and extensions of the most specific node. An ambiguous result adds candidates. owningCapabilityId is the nearest kind: capability node on the owner's parent chain (the owner itself when it is a capability), or null. For an ambiguous result it is set only when all candidates share one owning capability.",
|
|
63268
64711
|
exitCodes: "--path: 0 matched, 1 no match or error, 2 ambiguous. --paths-from: 0 when no path is ambiguous (unowned paths included), 2 when any path is ambiguous, 1 on error."
|
|
63269
64712
|
};
|
|
64713
|
+
function loadResolveModel(cwd) {
|
|
64714
|
+
if (!existsSync35(join27(cwd, ".archcontext/manifest.yaml"))) {
|
|
64715
|
+
throw new NativeContextError("native-context-model-missing", { path: ".archcontext/manifest.yaml" });
|
|
64716
|
+
}
|
|
64717
|
+
let model;
|
|
64718
|
+
try {
|
|
64719
|
+
model = loadNativeModelFromArchContext(cwd);
|
|
64720
|
+
} catch (error) {
|
|
64721
|
+
const cause = error;
|
|
64722
|
+
if (typeof cause?.code === "string") {
|
|
64723
|
+
throw new NativeContextError("native-context-input-unreadable", {
|
|
64724
|
+
category: "open-or-read",
|
|
64725
|
+
...typeof cause.path === "string" ? { path: cause.path } : {}
|
|
64726
|
+
});
|
|
64727
|
+
}
|
|
64728
|
+
throw new NativeContextError("native-context-model-invalid", { invariant: "model-parse" });
|
|
64729
|
+
}
|
|
64730
|
+
if (model.nodes.length === 0) {
|
|
64731
|
+
throw new NativeContextError("native-context-model-missing", { category: "no-native-nodes" });
|
|
64732
|
+
}
|
|
64733
|
+
for (const node of model.nodes) {
|
|
64734
|
+
const { issues } = validateJsonSchema(architecture_node_schema_default2, node);
|
|
64735
|
+
if (issues.length > 0) {
|
|
64736
|
+
throw new NativeContextError("native-context-model-invalid", {
|
|
64737
|
+
issues: issues.slice(0, 32).map((issue) => ({ entityId: typeof node.id === "string" ? node.id : "(undeclared)", field: issue.path, invariant: issue.message })),
|
|
64738
|
+
omittedDiagnosticCount: Math.max(0, issues.length - 32)
|
|
64739
|
+
});
|
|
64740
|
+
}
|
|
64741
|
+
}
|
|
64742
|
+
return model;
|
|
64743
|
+
}
|
|
63270
64744
|
function resolveResultData(path, outcome) {
|
|
63271
64745
|
if (outcome.status === "matched") {
|
|
63272
64746
|
const node = outcome.node;
|
|
@@ -63293,7 +64767,7 @@ async function readResolvePathsFrom(args, cwd, deps) {
|
|
|
63293
64767
|
return { problem: "resolve --paths-from requires a file path or - for stdin" };
|
|
63294
64768
|
let content;
|
|
63295
64769
|
try {
|
|
63296
|
-
content = source === "-" ? await (deps.readStdin ? deps.readStdin() :
|
|
64770
|
+
content = source === "-" ? await (deps.readStdin ? deps.readStdin() : readFileSync32(0, "utf8")) : readFileSync32(resolve35(cwd, source), "utf8");
|
|
63297
64771
|
} catch (error) {
|
|
63298
64772
|
return { problem: `resolve --paths-from ${source} is not readable: ${error instanceof Error ? error.message : String(error)}` };
|
|
63299
64773
|
}
|
|
@@ -63381,7 +64855,7 @@ function readRepositoryInputFile(cwd, flag, file) {
|
|
|
63381
64855
|
const absolute = assertPathHasNoSymlinkSegments2(cwd, file);
|
|
63382
64856
|
if (!statSync11(absolute).isFile())
|
|
63383
64857
|
return { problem: `plan ${flag} ${file} is not a regular file` };
|
|
63384
|
-
return { content:
|
|
64858
|
+
return { content: readFileSync32(absolute, "utf8") };
|
|
63385
64859
|
} catch (error) {
|
|
63386
64860
|
return { problem: `plan ${flag} ${file} is not readable: ${error instanceof Error ? error.message : String(error)}` };
|
|
63387
64861
|
}
|
|
@@ -63399,10 +64873,10 @@ function runHashCommand(args, cwd) {
|
|
|
63399
64873
|
} catch (error) {
|
|
63400
64874
|
return errorEnvelope("hash", "AC_SCHEMA_INVALID", error instanceof Error ? error.message : String(error));
|
|
63401
64875
|
}
|
|
63402
|
-
if (!
|
|
64876
|
+
if (!existsSync35(absolute) || !statSync11(absolute).isFile()) {
|
|
63403
64877
|
return errorEnvelope("hash", "AC_SCHEMA_INVALID", `hash --path is not an existing file: ${path}`);
|
|
63404
64878
|
}
|
|
63405
|
-
return okEnvelope("hash", { path, hash: digestJson({ body:
|
|
64879
|
+
return okEnvelope("hash", { path, hash: digestJson({ body: readFileSync32(absolute, "utf8") }) });
|
|
63406
64880
|
}
|
|
63407
64881
|
async function readCliPlanOperations(args, cwd, deps) {
|
|
63408
64882
|
if (args.includes("--operations-file")) {
|
|
@@ -63412,7 +64886,7 @@ async function readCliPlanOperations(args, cwd, deps) {
|
|
|
63412
64886
|
const file = readFlag(args, "--operations-file");
|
|
63413
64887
|
if (!file)
|
|
63414
64888
|
return "plan --operations-file requires a path";
|
|
63415
|
-
const read = file === "-" ? { content: await (deps.readStdin ? deps.readStdin() :
|
|
64889
|
+
const read = file === "-" ? { content: await (deps.readStdin ? deps.readStdin() : readFileSync32(0, "utf8")) } : readRepositoryInputFile(cwd, "--operations-file", file);
|
|
63416
64890
|
if ("problem" in read)
|
|
63417
64891
|
return read.problem;
|
|
63418
64892
|
let parsed;
|
|
@@ -63474,7 +64948,7 @@ async function readCliPlanBody(args, cwd, deps) {
|
|
|
63474
64948
|
return { problem: "plan --body requires a value (use - to read stdin)" };
|
|
63475
64949
|
if (inline !== "-")
|
|
63476
64950
|
return inline;
|
|
63477
|
-
return deps.readStdin ? deps.readStdin() :
|
|
64951
|
+
return deps.readStdin ? deps.readStdin() : readFileSync32(0, "utf8");
|
|
63478
64952
|
}
|
|
63479
64953
|
function readFlag(args, flag) {
|
|
63480
64954
|
const index = args.indexOf(flag);
|
|
@@ -63566,6 +65040,8 @@ if (__require.main == __require.module) {
|
|
|
63566
65040
|
await runStdioMcpLoop(stdinLines(), (line) => process.stdout.write(`${line}
|
|
63567
65041
|
`), (line) => process.stderr.write(`${line}
|
|
63568
65042
|
`), { runtimeResolver: (root, options) => createOrStartRuntimeRpcClient(root, options?.preserveHealthFailure) });
|
|
65043
|
+
} else if (command === "projection" && args[0] === "publish" && args.includes("--follow") && preflightCliArguments(command, args) === undefined) {
|
|
65044
|
+
process.exitCode = await runProjectionPublishFollowEntry(args.slice(1), process.cwd());
|
|
63569
65045
|
} else if (command === "daemon" && args[0] === "start" && args.includes("--foreground") && preflightCliArguments(command, args) === undefined) {
|
|
63570
65046
|
await runForegroundDaemon(process.cwd(), args).catch((error) => {
|
|
63571
65047
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -63579,6 +65055,8 @@ if (__require.main == __require.module) {
|
|
|
63579
65055
|
`);
|
|
63580
65056
|
if (result.ok === false)
|
|
63581
65057
|
process.exitCode = 1;
|
|
65058
|
+
if (command === "validate" && result.ok && result.data?.valid === false)
|
|
65059
|
+
process.exitCode = 1;
|
|
63582
65060
|
if (command === "resolve")
|
|
63583
65061
|
process.exitCode = resolveCommandExitCode(result);
|
|
63584
65062
|
}
|