archctx 0.8.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": "archcontext.practice-catalog-manifest/v1",
3
3
  "catalogVersion": "2026.06.0",
4
- "productVersion": "0.8.0",
4
+ "productVersion": "0.8.1",
5
5
  "generatedAt": "1970-01-01T00:00:00.000Z",
6
6
  "entries": [
7
7
  {
@@ -389,5 +389,5 @@
389
389
  "structurizr.dsl",
390
390
  "twelve-factor"
391
391
  ],
392
- "catalogDigest": "sha256:801316c8423f6d599e58a67ff0aa4cdd190fa105f263f0e636f5105709e325d2"
392
+ "catalogDigest": "sha256:abdff8c6f9f2ffc75fe35295b097884ec2996b179628bb81966f62cbb29ad75a"
393
393
  }
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" },
@@ -2031,6 +2032,23 @@ function dataRecord(value) {
2031
2032
  return fields;
2032
2033
  }
2033
2034
  function localFactValue(rule, value) {
2035
+ if (rule === "codeFactsIssues") {
2036
+ if (!Array.isArray(value))
2037
+ throw new Error("Code facts issues must be a list");
2038
+ const length = Object.getOwnPropertyDescriptor(value, "length")?.value;
2039
+ if (!Number.isSafeInteger(length) || length < 1 || length > 32 || Reflect.ownKeys(value).length !== length + 1)
2040
+ throw new Error("Code facts issues exceed their closed budget");
2041
+ return Array.from({ length }, (_, index) => {
2042
+ const entry = Object.getOwnPropertyDescriptor(value, String(index));
2043
+ if (!entry || !("value" in entry))
2044
+ throw new Error("Code facts issues must use own data");
2045
+ const fields = dataRecord(entry.value);
2046
+ const code = fields.code?.value, message = fields.message?.value;
2047
+ if (Object.keys(fields).length !== 2 || typeof code !== "string" || !Object.hasOwn(CODE_FACTS_ISSUE_MESSAGES, code) || message !== CODE_FACTS_ISSUE_MESSAGES[code])
2048
+ throw new Error("Unknown code facts diagnostic");
2049
+ return { code, message };
2050
+ });
2051
+ }
2034
2052
  if (rule === "commandSuggestion" && (value === null || typeof value === "string" && bytes(value) <= 1024 && /^archctx (recommendations |refactor verify )[^\r\n]*$/.test(value)))
2035
2053
  return value;
2036
2054
  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 +2148,7 @@ function nativeFactsFor(policy, input) {
2130
2148
  }
2131
2149
  const item = dataRecord(value);
2132
2150
  if (type === "issues") {
2133
- if (Object.keys(item).length !== 3 || !["entityId", "field", "invariant"].every((key) => Object.hasOwn(item, key)))
2151
+ if (!["entityId", "field", "invariant"].every((key) => Object.hasOwn(item, key)) || Object.keys(item).some((key) => !["entityId", "field", "invariant", "path"].includes(key)))
2134
2152
  throw new Error("Native issue schema is invalid");
2135
2153
  const clean = Object.fromEntries(Object.entries(item).map(([key, data]) => [key, text(data.value)]));
2136
2154
  if (Object.values(clean).some((v) => bytes(v) > 256)) {
@@ -2202,6 +2220,8 @@ function nativeFactsFor(policy, input) {
2202
2220
  }
2203
2221
  result[key] = values;
2204
2222
  }
2223
+ const sourceIssues = Array.isArray(result.issues) ? result.issues : [];
2224
+ const summarizePath = sourceIssues.some((issue) => typeof issue.path === "string");
2205
2225
  const counts = () => {
2206
2226
  if (!Number.isSafeInteger(omitted) || !Number.isSafeInteger(characters))
2207
2227
  throw new Error("Native diagnostic counts exceed schema");
@@ -2211,6 +2231,13 @@ function nativeFactsFor(policy, input) {
2211
2231
  result.omittedDiagnosticCharacters = characters;
2212
2232
  };
2213
2233
  const fits = () => {
2234
+ if (summarizePath) {
2235
+ const path = sourceIssues[0]?.path;
2236
+ if (typeof path === "string" && sourceIssues.every((issue) => issue.path === path))
2237
+ result.path = path;
2238
+ else
2239
+ delete result.path;
2240
+ }
2214
2241
  counts();
2215
2242
  const base = catalogDefaults[policy.code];
2216
2243
  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 +2352,21 @@ function normalizeFailureResult(result, stage, requestId = "failure") {
2325
2352
  return toEnvelope(requestId, undefined, "failure-metadata");
2326
2353
  }
2327
2354
  }
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;
2355
+ 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
2356
  var init_failure = __esm(() => {
2330
2357
  init_changeset_recovery();
2331
2358
  init_projection_recovery();
2332
2359
  init_schema();
2333
2360
  FAILURE_STAGES = Object.freeze(["debt", "complete", "practice-checkpoint", "cli", "mcp", "hook-checkpoint", "failure-metadata", "public-operation", "native-export", "docs-anchors", "flows-check"]);
2361
+ CODE_FACTS_ISSUE_MESSAGES = Object.freeze({
2362
+ "not-initialized": "CodeGraph repository status is not initialized",
2363
+ "version-mismatch": "CodeGraph repository status version mismatch",
2364
+ "project-path-mismatch": "CodeGraph repository project path mismatch",
2365
+ "index-incomplete": "CodeGraph repository index is not complete and compatible",
2366
+ "extraction-version-mismatch": "CodeGraph repository extraction version mismatch",
2367
+ "worktree-mismatch": "CodeGraph repository worktree mismatch",
2368
+ "pending-source-changes": "CodeGraph repository index has pending or unreported source changes"
2369
+ });
2334
2370
  policies = {
2335
2371
  "git-worktree-required": { code: "AC_REPO_NOT_FOUND", message: "This operation requires a Git worktree.", fields: ["checks"] },
2336
2372
  "model-missing": { code: "AC_MODEL_ADOPTION_REQUIRED", message: "Architecture debt requires an adopted model.", fields: ["checks"] },
@@ -2340,6 +2376,7 @@ var init_failure = __esm(() => {
2340
2376
  "projection-manifest-invalid": { code: "AC_PRECONDITION_FAILED", message: "The committed projection manifest violates its contract." },
2341
2377
  "projection-footprint-uncovered": { code: "AC_PRECONDITION_FAILED", message: "Declared source footprints lack a projection target." },
2342
2378
  "index-missing": { code: "AC_CODE_FACTS_UNAVAILABLE", message: "Required code facts have no available index. Run codegraph init." },
2379
+ "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
2380
  "checkpoint-input-invalid": { code: "AC_SCHEMA_INVALID", message: "Checkpoint arguments violate the declared input schema.", fields: ["issueCount"], action: "correct-command-arguments" },
2344
2381
  "cli-input-invalid": { code: "AC_SCHEMA_INVALID", message: "Command arguments violate the declared input contract.", action: "correct-command-arguments" },
2345
2382
  "complete-input-invalid": { code: "AC_SCHEMA_INVALID", message: "Completion input violates the caller authority contract.", action: "correct-command-arguments" },
@@ -2348,7 +2385,7 @@ var init_failure = __esm(() => {
2348
2385
  "runtime-version-unsupported": { code: "AC_RUNTIME_VERSION_UNSUPPORTED", message: "The connected runtime version is unsupported." },
2349
2386
  "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
2387
  "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. Inspect local diagnostics before retrying.", action: "inspect-diagnostics" }
2388
+ "failure-unclassified": { code: "AC_UNCLASSIFIED_FAILURE", message: "The operation failed without a classified cause. Run archctx doctor --json before retrying.", action: "inspect-diagnostics" }
2352
2389
  };
2353
2390
  Object.assign(policies, {
2354
2391
  "developer-review-run-not-owned": { code: "AC_PRECONDITION_FAILED", message: "developer-review-run-not-owned", facts: { refusal: "reviewOwnershipRefusal" } },
@@ -2585,7 +2622,7 @@ var init_failure = __esm(() => {
2585
2622
  }
2586
2623
  FAILURE_POLICIES = Object.freeze(policies);
2587
2624
  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] : [])]);
2625
+ 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
2626
  localEnums = {
2590
2627
  reviewWorktreeRefusal: ["UNKNOWN", "HEAD_UNAVAILABLE", "HEAD_SHA_MISMATCH", "TREE_OID_MISMATCH", "WORKTREE_NOT_DETACHED", "WORKTREE_NOT_CLEAN"],
2591
2628
  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 +3029,13 @@ function nativeAllowedNodeKinds(schemas) {
2992
3029
  throw new Error("canonical-node-kind-enum-unavailable");
2993
3030
  return [...kinds];
2994
3031
  }
2995
- function nativeContextModelIssues(model, schemas) {
3032
+ function nativeContextModelIssues(model, schemas, recordPaths) {
2996
3033
  const issues = [];
2997
- const add = (entityId, field, invariant) => issues.push({ entityId, field, invariant });
3034
+ const add = (record, field, invariant) => {
3035
+ const entityId = typeof record === "string" ? record : typeof record.id === "string" ? record.id : "(undeclared)";
3036
+ const path = typeof record === "string" ? undefined : recordPaths?.get(record);
3037
+ issues.push({ entityId, field, invariant, ...path === undefined ? {} : { path } });
3038
+ };
2998
3039
  const resolveSchema = nativeContextSchemaResolver(schemas);
2999
3040
  const ids = new Set;
3000
3041
  for (const [kind, records] of [["node", model.nodes], ["relation", model.relations], ["flow", model.flows]]) {
@@ -3013,14 +3054,14 @@ function nativeContextModelIssues(model, schemas) {
3013
3054
  continue;
3014
3055
  }
3015
3056
  if (typeof value === "number" && !Number.isFinite(value)) {
3016
- add(id, item.field, "non-json-number");
3057
+ add(record, item.field, "non-json-number");
3017
3058
  jsonInvalid = true;
3018
3059
  break;
3019
3060
  }
3020
3061
  if (value === null || typeof value !== "object")
3021
3062
  continue;
3022
3063
  if (active.has(value)) {
3023
- add(id, item.field, "non-json-cycle");
3064
+ add(record, item.field, "non-json-cycle");
3024
3065
  jsonInvalid = true;
3025
3066
  break;
3026
3067
  }
@@ -3028,7 +3069,7 @@ function nativeContextModelIssues(model, schemas) {
3028
3069
  continue;
3029
3070
  const prototype = Object.getPrototypeOf(value);
3030
3071
  if (Array.isArray(value) ? prototype !== Array.prototype : prototype !== Object.prototype) {
3031
- add(id, item.field, "non-json-prototype");
3072
+ add(record, item.field, "non-json-prototype");
3032
3073
  jsonInvalid = true;
3033
3074
  break;
3034
3075
  }
@@ -3041,36 +3082,35 @@ function nativeContextModelIssues(model, schemas) {
3041
3082
  continue;
3042
3083
  const schema = kind === "node" ? schemas.node : kind === "relation" ? schemas.relation : schemas.flow;
3043
3084
  for (const issue of validateJsonSchema(schema, record, { resolveSchema }).issues)
3044
- add(id, issue.path, issue.message);
3085
+ add(record, issue.path, issue.message);
3045
3086
  if (ids.has(id))
3046
- add(id, "id", "duplicate-entity-id");
3087
+ add(record, "id", "duplicate-entity-id");
3047
3088
  ids.add(id);
3048
3089
  }
3049
3090
  }
3050
3091
  if (issues.length > 0)
3051
3092
  return issues;
3052
3093
  for (const kind of ["nodes", "relations", "flows"]) {
3053
- const expected = new Set(model[kind].map((entity) => entity.id));
3054
- for (const id of expected)
3094
+ const expected = new Map(model[kind].map((entity) => [entity.id, entity]));
3095
+ for (const [id, entity] of expected)
3055
3096
  if (!Object.hasOwn(model.sourcePaths[kind], id))
3056
- add(id, `sourcePaths.${kind}`, "source-path-index-incomplete");
3097
+ add(entity, `sourcePaths.${kind}`, "source-path-index-incomplete");
3057
3098
  for (const [id, path] of Object.entries(model.sourcePaths[kind])) {
3058
3099
  if (!expected.has(id))
3059
3100
  add(id, `sourcePaths.${kind}`, "source-path-index-unknown-entity");
3060
3101
  if (!isNativeContextPath(path))
3061
- add(id, `sourcePaths.${kind}`, "path-unsafe");
3102
+ add(expected.get(id) ?? id, `sourcePaths.${kind}`, "path-unsafe");
3062
3103
  }
3063
3104
  }
3064
3105
  const nodes = new Map(model.nodes.map((node) => [node.id, node]));
3065
3106
  const checked = new Set;
3066
3107
  for (const node of model.nodes) {
3067
- const id = node.id;
3068
3108
  const chain = new Set;
3069
3109
  let cursor = node;
3070
3110
  while (cursor) {
3071
3111
  const current = cursor.id;
3072
3112
  if (chain.has(current)) {
3073
- add(id, "parent", "parent-cycle");
3113
+ add(node, "parent", "parent-cycle");
3074
3114
  break;
3075
3115
  }
3076
3116
  if (checked.has(current))
@@ -3080,7 +3120,7 @@ function nativeContextModelIssues(model, schemas) {
3080
3120
  break;
3081
3121
  const parent = nodes.get(cursor.parent);
3082
3122
  if (!parent) {
3083
- add(current, "parent", "parent-node-missing");
3123
+ add(cursor, "parent", "parent-node-missing");
3084
3124
  break;
3085
3125
  }
3086
3126
  cursor = parent;
@@ -3093,17 +3133,17 @@ function nativeContextModelIssues(model, schemas) {
3093
3133
  const source = node.source;
3094
3134
  for (const path of [...source?.include ?? [], ...source?.exclude ?? []])
3095
3135
  if (!isNativeContextPath(path))
3096
- add(node.id, "source", "path-unsafe");
3136
+ add(node, "source", "path-unsafe");
3097
3137
  for (const entry of source?.entrypoints ?? []) {
3098
3138
  if (!isNativeContextPath(entry.path))
3099
- add(node.id, "source.entrypoints.path", "path-unsafe");
3139
+ add(node, "source.entrypoints.path", "path-unsafe");
3100
3140
  for (const symbol of entry.symbols) {
3101
3141
  for (const sink of symbol.sinks) {
3102
3142
  if (!isNativeContextPath(sink.path))
3103
- add(node.id, "source.entrypoints.symbols.sinks.path", "path-unsafe");
3143
+ add(node, "source.entrypoints.symbols.sinks.path", "path-unsafe");
3104
3144
  const selector = JSON.stringify([entry.id, symbol.name, sink.id]);
3105
3145
  if (selectors.has(selector))
3106
- add(node.id, "source.entrypoints.symbols.sinks.id", "selector-ambiguous");
3146
+ add(node, "source.entrypoints.symbols.sinks.id", "selector-ambiguous");
3107
3147
  selectors.add(selector);
3108
3148
  }
3109
3149
  }
@@ -3112,43 +3152,42 @@ function nativeContextModelIssues(model, schemas) {
3112
3152
  for (const relation of model.relations) {
3113
3153
  for (const field of ["source", "target", "via"])
3114
3154
  if (relation[field] !== undefined && !nodes.has(relation[field]))
3115
- add(relation.id, field, "relation-node-missing");
3155
+ add(relation, field, "relation-node-missing");
3116
3156
  }
3117
3157
  for (const flow of model.flows) {
3118
- const id = flow.id;
3119
3158
  if (nodes.get(flow.capabilityId)?.kind !== "capability")
3120
- add(id, "capabilityId", "flow-capability-missing");
3159
+ add(flow, "capabilityId", "flow-capability-missing");
3121
3160
  if (flow.applicability !== "required")
3122
3161
  continue;
3123
3162
  const participants = flow.participants;
3124
3163
  const participantIds = new Set;
3125
3164
  for (const participant of participants) {
3126
3165
  if (participantIds.has(participant.id))
3127
- add(id, "participants.id", "participant-id-ambiguous");
3166
+ add(flow, "participants.id", "participant-id-ambiguous");
3128
3167
  participantIds.add(participant.id);
3129
3168
  if (!nodes.has(participant.nodeId))
3130
- add(id, "participants.nodeId", "participant-node-missing");
3169
+ add(flow, "participants.nodeId", "participant-node-missing");
3131
3170
  }
3132
3171
  const definitions = new Map;
3133
3172
  const outcomes = flow.outcomes;
3134
3173
  const outcomeIds = new Set;
3135
3174
  for (const outcome of outcomes) {
3136
3175
  if (outcomeIds.has(outcome.id))
3137
- add(id, "outcomes.id", "outcome-id-ambiguous");
3176
+ add(flow, "outcomes.id", "outcome-id-ambiguous");
3138
3177
  outcomeIds.add(outcome.id);
3139
3178
  if (!participantIds.has(outcome.terminal.participant))
3140
- add(id, "outcomes.terminal", "terminal-participant-missing");
3179
+ add(flow, "outcomes.terminal", "terminal-participant-missing");
3141
3180
  }
3142
3181
  for (const step of [...flow.steps, ...outcomes.flatMap((outcome) => outcome.steps)]) {
3143
3182
  if (!participantIds.has(step.from) || !participantIds.has(step.to))
3144
- add(id, "steps", "step-participant-missing");
3183
+ add(flow, "steps", "step-participant-missing");
3145
3184
  const definition = canonicalize(step);
3146
3185
  if (definitions.has(step.id) && definitions.get(step.id) !== definition)
3147
- add(id, "steps.id", "step-definition-ambiguous");
3186
+ add(flow, "steps.id", "step-definition-ambiguous");
3148
3187
  definitions.set(step.id, definition);
3149
3188
  const selector = step.evidence;
3150
3189
  if (!selectors.has(JSON.stringify([selector.entrypointId, selector.sourceSymbol, selector.sinkId])))
3151
- add(id, "steps.evidence", "selector-missing");
3190
+ add(flow, "steps.evidence", "selector-missing");
3152
3191
  }
3153
3192
  }
3154
3193
  return issues;
@@ -3828,7 +3867,7 @@ function compareVersionTriples(left, right) {
3828
3867
  }
3829
3868
  return 0;
3830
3869
  }
3831
- var ARCHCONTEXT_PRODUCT_NAME = "archctx", ARCHCONTEXT_PRODUCT_VERSION = "0.8.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";
3870
+ var ARCHCONTEXT_PRODUCT_NAME = "archctx", ARCHCONTEXT_PRODUCT_VERSION = "0.8.1", 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
3871
 
3833
3872
  // packages/contracts/src/refactor.ts
3834
3873
  function refactorNamespaceIssues(value, label = "product.id") {
@@ -15748,10 +15787,10 @@ function sourceIncludeAnchors(patterns) {
15748
15787
  init_src();
15749
15788
 
15750
15789
  // packages/core/architecture-domain/src/index.ts
15751
- init_src();
15752
15790
  import { createHash as createHash2 } from "node:crypto";
15753
15791
  import { readFileSync, realpathSync, statSync } from "node:fs";
15754
15792
  import { resolve } from "node:path";
15793
+ init_src();
15755
15794
  var LANDSCAPE_FILE = ".archcontext/landscape.yaml";
15756
15795
  var ADAPTER_PROTECTED_NATIVE_FIELDS = ["evidence", "verification", "constraint", "intervention"];
15757
15796
  var DEFAULT_IGNORES = new Set([
@@ -15980,7 +16019,7 @@ function parseJsonOrStableYaml(body, path) {
15980
16019
  if (!trimmed)
15981
16020
  throw new Error(`${path}: empty model file`);
15982
16021
  if (trimmed.startsWith("{") || trimmed.startsWith("["))
15983
- return checkedJsonDecode(trimmed, path, "json-document");
16022
+ return checkedJsonOrFlowYaml(trimmed, path, "json-document");
15984
16023
  return new StableYamlParser(body, path).parse();
15985
16024
  }
15986
16025
  var ADR_FILE_PATH = /^docs\/adr\/ADR-\d{4}-.+\.md$/;
@@ -16361,7 +16400,7 @@ function parseScalar(value, path) {
16361
16400
  if (/^-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?$/.test(value))
16362
16401
  return Number(value);
16363
16402
  if (/^["[{]/.test(value))
16364
- return checkedJsonDecode(value, path, "json-scalar");
16403
+ return checkedJsonOrFlowYaml(value, path, "json-scalar");
16365
16404
  return value;
16366
16405
  }
16367
16406
 
@@ -16379,6 +16418,54 @@ class DuplicateModelKeyError extends Error {
16379
16418
  this.name = "DuplicateModelKeyError";
16380
16419
  }
16381
16420
  }
16421
+ function checkedJsonOrFlowYaml(source, path, branch) {
16422
+ try {
16423
+ return checkedJsonDecode(source, path, branch);
16424
+ } catch (error) {
16425
+ if (!(error instanceof SyntaxError) || !(source.startsWith("{") || source.startsWith("[")))
16426
+ throw error;
16427
+ }
16428
+ return checkedFlowYamlDecode(source, path);
16429
+ }
16430
+ function checkedFlowYamlDecode(source, path) {
16431
+ const document = $parseDocument(source, { version: "1.2", schema: "core", uniqueKeys: false, prettyErrors: false });
16432
+ const invalid = (reason) => {
16433
+ throw new SyntaxError(`${path}: invalid YAML flow collection (${reason})`);
16434
+ };
16435
+ if (document.errors.length || document.warnings.length)
16436
+ invalid("parse error or warning");
16437
+ if (!$isMap(document.contents) && !$isSeq(document.contents) || !document.contents.flow)
16438
+ invalid("expected flow collection");
16439
+ const decode = (node) => {
16440
+ if (node === null)
16441
+ return null;
16442
+ if (!$isScalar(node) && !$isMap(node) && !$isSeq(node))
16443
+ return invalid("unsupported node or alias");
16444
+ if (node.anchor || node.tag)
16445
+ return invalid("anchor or explicit tag");
16446
+ if ($isScalar(node)) {
16447
+ const value = node.value;
16448
+ if (value === null || typeof value === "string" || typeof value === "boolean")
16449
+ return value;
16450
+ if (typeof value === "number" && Number.isFinite(value))
16451
+ return value;
16452
+ return invalid("non-JSON scalar");
16453
+ }
16454
+ if ($isSeq(node))
16455
+ return node.items.map(decode);
16456
+ const keys = new Set;
16457
+ return Object.fromEntries(node.items.map((pair) => {
16458
+ if (!$isScalar(pair.key))
16459
+ return invalid("non-scalar key");
16460
+ const key = String(decode(pair.key));
16461
+ if (keys.has(key))
16462
+ throw new DuplicateModelKeyError(path, "yaml-flow", key, pair.key.range[0]);
16463
+ keys.add(key);
16464
+ return [key, decode(pair.value)];
16465
+ }));
16466
+ };
16467
+ return decode(document.contents);
16468
+ }
16382
16469
  function checkedJsonDecode(source, path, branch) {
16383
16470
  let offset = 0;
16384
16471
  const whitespace = () => {
@@ -16968,6 +17055,12 @@ function renderAdrReferenceUpdate(body, references) {
16968
17055
  return rendered;
16969
17056
  }
16970
17057
  // packages/core/changeset-engine/src/index.ts
17058
+ function changeSetBaseModelBlockingErrors(result, draft) {
17059
+ const repairedPaths = new Set(draft.operations.filter((operation) => operation.op === "update_entity_fields" || operation.op === "delete_entity").map((operation) => operation.path));
17060
+ const repairedErrors = new Set((result.nodeSchemaErrors ?? []).filter((issue) => repairedPaths.has(issue.path)).map((issue) => issue.error));
17061
+ return baseModelBlockingErrors(result).filter((error) => !repairedErrors.has(error));
17062
+ }
17063
+
16971
17064
  class ChangeSetEngine {
16972
17065
  deps;
16973
17066
  states = new Map;
@@ -17190,10 +17283,16 @@ class ChangeSetEngine {
17190
17283
  }
17191
17284
  async validateModel(root, draft, deps, phase) {
17192
17285
  const result = await deps.modelStore.validateModel({ root, repositoryId: draft.reason.taskSessionId, headSha: draft.base.headSha });
17193
- const blocked = phase === "before" ? baseModelBlockingErrors(result).length > 0 : !result.valid;
17286
+ const errors = phase === "before" ? changeSetBaseModelBlockingErrors(result, draft) : result.errors;
17287
+ const blocked = phase === "before" ? errors.length > 0 : !result.valid;
17194
17288
  if (blocked) {
17195
- const errors = phase === "before" ? baseModelBlockingErrors(result) : result.errors;
17196
- throw new Error(`ChangeSet model validation failed ${phase} apply: ${errors.join("; ") || "unknown validation error"}`);
17289
+ const path = errors[0]?.match(/^(\.archcontext\/.+?): /)?.[1];
17290
+ const error = new FailureError("AC_SCHEMA_INVALID", "native-context-model-invalid", {
17291
+ invariant: `model-validation-${phase}-apply`,
17292
+ ...path ? { path } : {}
17293
+ });
17294
+ error.message = `ChangeSet model validation failed ${phase} apply: ${errors.join("; ") || "unknown validation error"}`;
17295
+ throw error;
17197
17296
  }
17198
17297
  }
17199
17298
  requireDeps() {
@@ -22897,9 +22996,6 @@ function isGitWorktreeError(error) {
22897
22996
  return error instanceof Error && /git|Command failed/.test(error.message);
22898
22997
  }
22899
22998
 
22900
- // packages/local-runtime/runtime-daemon/src/producer-failure.ts
22901
- init_src();
22902
-
22903
22999
  // packages/local-runtime/egress-admission/src/index.ts
22904
23000
  var LOCAL_EGRESS_MODE_ENV2 = "ARCHCONTEXT_EGRESS_MODE";
22905
23001
  var LOCAL_EGRESS_CHANNELS2 = ["context7", "agent-audit", "github-issue-publishing", "npm-update-check", "codegraph-telemetry"];
@@ -22931,6 +23027,7 @@ function withLocalEgress2(channel, operation, env = process.env) {
22931
23027
  }
22932
23028
 
22933
23029
  // packages/local-runtime/codegraph-adapter/src/index.ts
23030
+ init_src();
22934
23031
  import { execFileSync as execFileSync2 } from "node:child_process";
22935
23032
  import { createHash as createHash5 } from "node:crypto";
22936
23033
  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 +24029,15 @@ class CodeGraphCliProvider {
23932
24029
  }
23933
24030
  async indexAll(workspaceRoot) {
23934
24031
  this.workspaceRoot = workspaceRoot;
23935
- if (!existsSync6(join3(workspaceRoot, ".codegraph"))) {
23936
- throw new Error(`CodeGraph index missing at ${workspaceRoot}; run codegraph init before using archctxd`);
23937
- }
24032
+ this.assertCanSync(workspaceRoot);
23938
24033
  this.run(["sync", workspaceRoot]);
24034
+ this.assertReady(workspaceRoot);
24035
+ }
24036
+ assertReady(workspaceRoot) {
24037
+ assertRepositoryCodeFactsReady(workspaceRoot, this.binary === DEFAULT_CODEGRAPH_BINARY ? {} : { binary: this.binary });
24038
+ }
24039
+ assertCanSync(workspaceRoot) {
24040
+ this.assertReady(workspaceRoot);
23939
24041
  }
23940
24042
  async buildContext(task, options) {
23941
24043
  const query = scopedTaskQuery(task, options.changedPaths ?? []);
@@ -23998,7 +24100,7 @@ class CodeGraphCliProvider {
23998
24100
  }
23999
24101
  run(args) {
24000
24102
  disableCodeGraphTelemetryByDefault();
24001
- const invocation = codeGraphCliInvocation(this.binary, this.workspaceRoot);
24103
+ const invocation = this.binary === DEFAULT_CODEGRAPH_BINARY ? packagedCodeGraphCliInvocation() : codeGraphCliInvocation(this.binary, this.workspaceRoot);
24002
24104
  try {
24003
24105
  return execFileSync2(invocation.command, [...invocation.argsPrefix, ...args], {
24004
24106
  cwd: this.workspaceRoot,
@@ -24129,8 +24231,14 @@ class CodeGraphAdapter {
24129
24231
  this.provider = provider;
24130
24232
  disableCodeGraphTelemetryByDefault();
24131
24233
  }
24234
+ assertCanSync(workspaceRoot) {
24235
+ this.assertCompatible();
24236
+ this.provider.assertCanSync?.(workspaceRoot);
24237
+ }
24132
24238
  async ensureReady(workspace) {
24133
24239
  this.assertCompatible();
24240
+ if (this.#snapshot)
24241
+ this.provider.assertReady?.(workspace.root);
24134
24242
  this.#snapshot ??= await this.sync({ workspace });
24135
24243
  return this.#snapshot;
24136
24244
  }
@@ -24594,7 +24702,7 @@ function readProjectionHeadSha(root) {
24594
24702
  function projectionCodeGraphStatusIssues(status, root, phase) {
24595
24703
  const issue = (code, text) => ({
24596
24704
  code,
24597
- message: `CodeGraph ${phase} ${text}`
24705
+ message: phase === "repository" ? CODE_FACTS_ISSUE_MESSAGES[code] : `CodeGraph ${phase} ${text}`
24598
24706
  });
24599
24707
  if (!status.initialized)
24600
24708
  return [issue("not-initialized", "status is not initialized")];
@@ -24703,7 +24811,7 @@ function inspectRepositoryCodeFacts(root, options = {}) {
24703
24811
  message: (error instanceof Error ? error.message : String(error)).replace(/\s+/g, " ").trim().slice(0, 400)
24704
24812
  };
24705
24813
  }
24706
- const versionIssues = codeGraphVersion === REQUIRED_CODEGRAPH_VERSION ? [] : [{ code: "version-mismatch", message: `CodeGraph ${REQUIRED_CODEGRAPH_VERSION} required, got actual binary ${codeGraphVersion || "unknown"}` }];
24814
+ const versionIssues = codeGraphVersion === REQUIRED_CODEGRAPH_VERSION ? [] : [{ code: "version-mismatch", message: CODE_FACTS_ISSUE_MESSAGES["version-mismatch"] }];
24707
24815
  if (!indexPresent) {
24708
24816
  return { schemaVersion, state: "index-missing", indexPresent: false, requiredVersion: REQUIRED_CODEGRAPH_VERSION, codeGraphVersion, issues: versionIssues };
24709
24817
  }
@@ -24733,12 +24841,25 @@ function inspectRepositoryCodeFacts(root, options = {}) {
24733
24841
  current: status.index?.currentExtractionVersion ?? null
24734
24842
  },
24735
24843
  indexState: status.index?.state ?? null,
24736
- reindexRecommended: status.index?.reindexRecommended ?? null,
24844
+ reindexRecommended: issues.some((issue) => issue.code !== "pending-source-changes"),
24737
24845
  pendingRefs: status.index?.pendingRefs ?? null,
24738
24846
  pendingChanges: pending ? { added: pending.added, modified: pending.modified, removed: pending.removed } : null,
24739
24847
  issues
24740
24848
  };
24741
24849
  }
24850
+ function assertRepositoryCodeFactsReady(root, options = {}) {
24851
+ const facts = inspectRepositoryCodeFacts(root, options);
24852
+ if (facts.state === "index-missing")
24853
+ throw new ProjectionCodeFactsUnavailableError("index-missing");
24854
+ if (facts.state === "unavailable")
24855
+ throw new FailureError("AC_CODE_FACTS_UNAVAILABLE", "code-facts-unavailable", {}, { action: "inspect-diagnostics", retryable: false });
24856
+ if (facts.state === "not-ready") {
24857
+ if (facts.issues.every((issue) => issue.code === "pending-source-changes"))
24858
+ return;
24859
+ const action = facts.codeGraphVersion !== REQUIRED_CODEGRAPH_VERSION ? "install-compatible-codegraph" : "codegraph-index";
24860
+ throw new FailureError("AC_CODE_FACTS_NOT_READY", "code-facts-not-ready", { issues: facts.issues }, { action });
24861
+ }
24862
+ }
24742
24863
  function codeGraphIndexAvailable(workspaceRoot) {
24743
24864
  return existsSync6(join3(workspaceRoot, ".codegraph"));
24744
24865
  }
@@ -25061,6 +25182,7 @@ function parseExploreSymbols(output, maxSymbols) {
25061
25182
  }
25062
25183
 
25063
25184
  // packages/local-runtime/runtime-daemon/src/producer-failure.ts
25185
+ init_src();
25064
25186
  function selectedProducerData(error, key) {
25065
25187
  const descriptor = Object.getOwnPropertyDescriptor(error, key);
25066
25188
  if (!descriptor || !("value" in descriptor))
@@ -26151,12 +26273,6 @@ function isPresent2(value) {
26151
26273
  init_src();
26152
26274
  import { existsSync as existsSync7, lstatSync as lstatSync5, mkdirSync as mkdirSync4, readdirSync as readdirSync4, readFileSync as readFileSync8, rmSync as rmSync4 } from "node:fs";
26153
26275
  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
26276
  // schemas/repo/architecture-node.schema.json
26161
26277
  var architecture_node_schema_default = {
26162
26278
  $schema: "https://json-schema.org/draft/2020-12/schema",
@@ -26245,6 +26361,12 @@ var architecture_node_schema_default = {
26245
26361
  extensions: { type: "object", additionalProperties: true }
26246
26362
  }
26247
26363
  };
26364
+
26365
+ // packages/local-runtime/model-store-yaml/src/native-context.ts
26366
+ init_src();
26367
+ import { createHash as createHash6 } from "node:crypto";
26368
+ import { constants, closeSync as closeSync4, fstatSync as fstatSync2, lstatSync as lstatSync4, openSync as openSync4, readSync as readSync2, realpathSync as realpathSync4 } from "node:fs";
26369
+ import { join as join4, resolve as resolve10 } from "node:path";
26248
26370
  // schemas/repo/architecture-relation.schema.json
26249
26371
  var architecture_relation_schema_default = {
26250
26372
  $schema: "https://json-schema.org/draft/2020-12/schema",
@@ -26751,6 +26873,7 @@ function capture(source, snapshot) {
26751
26873
  if (typeof budget !== "number" || !Number.isSafeInteger(budget) || budget <= 0)
26752
26874
  throw new NativeContextError("native-context-budget-invalid", { path: MANIFEST, field: "runtime.contextBudgetBytes" });
26753
26875
  const model = { nodes: [], relations: [], flows: [], sourcePaths: { nodes: {}, relations: {}, flows: {} } };
26876
+ const recordPaths = new Map;
26754
26877
  for (const spec of specs)
26755
26878
  if (spec.role !== "manifest") {
26756
26879
  const value = record5(parseInput(source, spec.path));
@@ -26758,10 +26881,11 @@ function capture(source, snapshot) {
26758
26881
  throw new NativeContextError("native-context-model-invalid", { path: spec.path, invariant: "entity-object-required" });
26759
26882
  const kind = spec.role === "node" ? "nodes" : spec.role === "relation" ? "relations" : "flows";
26760
26883
  model[kind].push(value);
26884
+ recordPaths.set(value, spec.path);
26761
26885
  if (typeof value.id === "string")
26762
26886
  model.sourcePaths[kind][value.id] = spec.path;
26763
26887
  }
26764
- const issues = nativeContextModelIssues(model, SCHEMAS);
26888
+ const issues = nativeContextModelIssues(model, SCHEMAS, recordPaths);
26765
26889
  if (issues.length > 0)
26766
26890
  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
26891
  for (const kind of MODEL_KINDS)
@@ -27100,6 +27224,7 @@ class YamlModelStore {
27100
27224
  }
27101
27225
  async validateModel(workspace) {
27102
27226
  const errors = [];
27227
+ const nodeSchemaErrors = [];
27103
27228
  for (const required of [".archcontext/manifest.yaml", ".archcontext/product.yaml"]) {
27104
27229
  try {
27105
27230
  readFileSync8(resolve11(workspace.root, required), "utf8");
@@ -27109,11 +27234,25 @@ class YamlModelStore {
27109
27234
  }
27110
27235
  const files = listModelFiles(workspace.root);
27111
27236
  for (const file of files) {
27237
+ if (file.path.startsWith(".archcontext/model/") && /\.ya?ml$/.test(file.path)) {
27238
+ try {
27239
+ const value = parseJsonOrStableYaml(file.body, file.path);
27240
+ if (file.path.startsWith(".archcontext/model/nodes/")) {
27241
+ const { issues } = validateJsonSchema(architecture_node_schema_default, value);
27242
+ for (const issue of issues) {
27243
+ const error = `${file.path}: ${issue.path}: ${issue.message}`;
27244
+ errors.push(error);
27245
+ nodeSchemaErrors.push({ path: file.path, error });
27246
+ }
27247
+ continue;
27248
+ }
27249
+ } catch (error) {
27250
+ errors.push(`${file.path}: invalid model YAML: ${error instanceof Error ? error.message : String(error)}`);
27251
+ continue;
27252
+ }
27253
+ }
27112
27254
  if (!file.schemaVersion.startsWith("archcontext."))
27113
27255
  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
27256
  if (file.path.startsWith(".archcontext/model/flows/") && file.schemaVersion !== "archcontext.flow/v1") {
27118
27257
  errors.push(`${file.path}: expected archcontext.flow/v1, got ${file.schemaVersion || "missing"}`);
27119
27258
  }
@@ -27129,6 +27268,7 @@ class YamlModelStore {
27129
27268
  valid: errors.length === 0,
27130
27269
  errors,
27131
27270
  ...referenceErrors.length > 0 ? { referenceErrors } : {},
27271
+ ...nodeSchemaErrors.length > 0 ? { nodeSchemaErrors } : {},
27132
27272
  ...warnings.length > 0 ? { warnings } : {},
27133
27273
  modelDigest
27134
27274
  };
@@ -34042,7 +34182,7 @@ function readArchitectureDebtEvidence(root, input) {
34042
34182
  capabilityByNodeId
34043
34183
  } };
34044
34184
  }
34045
- function runArchitectureDebtCommand(cwd, input = {}) {
34185
+ function runArchitectureDebtCommand(cwd, input = {}, codeFactsOptions = {}) {
34046
34186
  let root;
34047
34187
  try {
34048
34188
  root = findRepositoryRoot(cwd);
@@ -34050,6 +34190,8 @@ function runArchitectureDebtCommand(cwd, input = {}) {
34050
34190
  return failureEnvelope("debt", knownFailure("AC_REPO_NOT_FOUND", "git-worktree-required", { checks: ["ownership", "anchors"] }));
34051
34191
  }
34052
34192
  try {
34193
+ if (codeGraphIndexAvailable(root))
34194
+ assertRepositoryCodeFactsReady(root, codeFactsOptions);
34053
34195
  return okEnvelope("debt", readArchitectureDebt(root, input));
34054
34196
  } catch (error) {
34055
34197
  return selectedProducerFailureEnvelope("debt", error, "debt");
@@ -34596,10 +34738,10 @@ function assertAllowedArchContextPath2(root, relativePath, scope) {
34596
34738
  }
34597
34739
 
34598
34740
  // packages/core/architecture-domain/src/index.ts
34599
- init_src();
34600
34741
  import { createHash as createHash9 } from "node:crypto";
34601
34742
  import { readFileSync as readFileSync14, realpathSync as realpathSync7, statSync as statSync4 } from "node:fs";
34602
34743
  import { resolve as resolve18 } from "node:path";
34744
+ init_src();
34603
34745
  var DEFAULT_IGNORES2 = new Set([
34604
34746
  ".git",
34605
34747
  ".codegraph",
@@ -34818,7 +34960,10 @@ var WINDOWS_CREATE_TIMEOUT_MS = 60000;
34818
34960
  var WINDOWS_READ_TIMEOUT_MS = 1e4;
34819
34961
  var REMOVE_HANDLE_RELEASE_MARGIN_MS = 2000;
34820
34962
  var REMOVE_RETRY_DELAY_MS = 25;
34963
+ var WINDOWS_CONTROL_FILE_PHASES = ["script", "input", "parsed", "opened", "verified", "done"];
34964
+ var WINDOWS_CONTROL_FILE_PHASE_MARKER = /^archctx-acl-phase:([a-z]+)\r?$/;
34821
34965
  var WINDOWS_CONTROL_FILE_SCRIPT = String.raw`
34966
+ [Console]::Error.WriteLine('archctx-acl-phase:script')
34822
34967
  $ErrorActionPreference = 'Stop'
34823
34968
  # A pwsh -> Node/Bun -> powershell.exe launch inherits incompatible PS7 module paths.
34824
34969
  $env:PSModulePath = "$PSHOME\Modules"
@@ -34840,7 +34985,10 @@ function Test-PrivateAcl($stream, $sid) {
34840
34985
  $rule.PropagationFlags -eq [System.Security.AccessControl.PropagationFlags]::None)
34841
34986
  }
34842
34987
  try {
34843
- $request = [Console]::In.ReadToEnd() | ConvertFrom-Json
34988
+ $text = [Console]::In.ReadToEnd()
34989
+ [Console]::Error.WriteLine('archctx-acl-phase:input')
34990
+ $request = $text | ConvertFrom-Json
34991
+ [Console]::Error.WriteLine('archctx-acl-phase:parsed')
34844
34992
  $sid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User
34845
34993
  if ($request.operation -eq 'create') {
34846
34994
  $acl = New-Object System.Security.AccessControl.FileSecurity
@@ -34850,18 +34998,24 @@ try {
34850
34998
  $acl.AddAccessRule($rule)
34851
34999
  $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
35000
  $created = $true
35001
+ [Console]::Error.WriteLine('archctx-acl-phase:opened')
34853
35002
  if (-not (Test-PrivateAcl $stream $sid)) { throw 'private ACL readback failed' }
35003
+ [Console]::Error.WriteLine('archctx-acl-phase:verified')
34854
35004
  $bytes = [System.Text.Encoding]::UTF8.GetBytes([string]$request.body)
34855
35005
  $stream.Write($bytes, 0, $bytes.Length)
34856
35006
  $stream.Flush($true)
35007
+ [Console]::Error.WriteLine('archctx-acl-phase:done')
34857
35008
  $result = @{ ok = $true }
34858
35009
  } elseif ($request.operation -eq 'read') {
34859
35010
  # Deny concurrent write/delete, validate the opened file, then read that same handle.
34860
35011
  $stream = New-Object System.IO.FileStream($request.path, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::Read)
35012
+ [Console]::Error.WriteLine('archctx-acl-phase:opened')
34861
35013
  if (-not (Test-PrivateAcl $stream $sid)) { throw 'private ACL readback failed' }
35014
+ [Console]::Error.WriteLine('archctx-acl-phase:verified')
34862
35015
  if ($stream.Length -gt 1048576) { throw 'control file exceeds read budget' }
34863
35016
  $reader = New-Object System.IO.StreamReader($stream, [System.Text.Encoding]::UTF8, $true, 4096, $true)
34864
35017
  try { $body = $reader.ReadToEnd() } finally { $reader.Dispose() }
35018
+ [Console]::Error.WriteLine('archctx-acl-phase:done')
34865
35019
  $result = @{ ok = $true; body = $body }
34866
35020
  } else { throw 'unsupported operation' }
34867
35021
  [Console]::Out.Write(($result | ConvertTo-Json -Compress))
@@ -34903,8 +35057,9 @@ function windowsControlFile(operation, path, body) {
34903
35057
  const code = ["ETIMEDOUT", "ENOENT", "EACCES", "EPERM", "ENOBUFS"].includes(String(failure?.code)) ? String(failure?.code) : "UNKNOWN";
34904
35058
  const signal = ["SIGTERM", "SIGKILL", "SIGABRT", "SIGINT"].includes(String(failure?.signal)) ? String(failure?.signal) : null;
34905
35059
  const status = typeof failure?.status === "number" && Number.isInteger(failure.status) ? failure.status : null;
35060
+ const phase = lastWindowsControlFilePhase(failure?.stderr);
34906
35061
  const elapsedMs = Math.round(performance.now() - startedAt);
34907
- throw new Error(`Windows native ACL operation failed: ${JSON.stringify({ operation, code, status, signal, elapsedMs })}`);
35062
+ throw new Error(`Windows native ACL operation failed: ${JSON.stringify({ operation, code, status, signal, phase, elapsedMs })}`);
34908
35063
  }
34909
35064
  const result = JSON.parse(output);
34910
35065
  if (!result || typeof result !== "object" || !("ok" in result))
@@ -34919,6 +35074,18 @@ function windowsControlFile(operation, path, body) {
34919
35074
  return result.body;
34920
35075
  }
34921
35076
  }
35077
+ function lastWindowsControlFilePhase(stderr) {
35078
+ let phase = "none";
35079
+ if (typeof stderr !== "string")
35080
+ return phase;
35081
+ for (const line of stderr.split(`
35082
+ `)) {
35083
+ const name = WINDOWS_CONTROL_FILE_PHASE_MARKER.exec(line)?.[1];
35084
+ if (name && WINDOWS_CONTROL_FILE_PHASES.includes(name))
35085
+ phase = name;
35086
+ }
35087
+ return phase;
35088
+ }
34922
35089
  function createPrivateControlFile(path, body) {
34923
35090
  if (process.platform === "win32") {
34924
35091
  windowsControlFile("create", path, body);
@@ -37988,7 +38155,7 @@ function readArchitectureDebtEvidence2(root, input) {
37988
38155
  capabilityByNodeId
37989
38156
  } };
37990
38157
  }
37991
- function runArchitectureDebtCommand2(cwd, input = {}) {
38158
+ function runArchitectureDebtCommand2(cwd, input = {}, codeFactsOptions = {}) {
37992
38159
  let root;
37993
38160
  try {
37994
38161
  root = findRepositoryRoot(cwd);
@@ -37996,6 +38163,8 @@ function runArchitectureDebtCommand2(cwd, input = {}) {
37996
38163
  return failureEnvelope("debt", knownFailure("AC_REPO_NOT_FOUND", "git-worktree-required", { checks: ["ownership", "anchors"] }));
37997
38164
  }
37998
38165
  try {
38166
+ if (codeGraphIndexAvailable(root))
38167
+ assertRepositoryCodeFactsReady(root, codeFactsOptions);
37999
38168
  return okEnvelope("debt", readArchitectureDebt2(root, input));
38000
38169
  } catch (error) {
38001
38170
  return selectedProducerFailureEnvelope("debt", error, "debt");
@@ -42988,7 +43157,13 @@ class PracticeCheckpointService {
42988
43157
  }
42989
43158
  async prepareUnchecked(root, task, maxBytes = 12288, maxItems = 12, taskSessionId = "task_runtime") {
42990
43159
  this.assertRunning();
43160
+ if (this.codeFacts.assertCanSync) {
43161
+ root = findRepositoryRoot(root);
43162
+ this.codeFacts.assertCanSync(root);
43163
+ }
42991
43164
  const session = await this.openSession(root);
43165
+ if (this.codeFacts.assertCanSync)
43166
+ await this.codeFacts.ensureReady(session.workspace);
42992
43167
  const result = await prepareTask({
42993
43168
  workspace: session.workspace,
42994
43169
  task,
@@ -56409,7 +56584,7 @@ class ChangeSetAuthorityService {
56409
56584
  if (draft.base.worktreeDigest !== current)
56410
56585
  throw new FailureError("AC_PRECONDITION_FAILED", "apply-worktree-changed", { expectedWorktreeDigest: draft.base.worktreeDigest, actualWorktreeDigest: current });
56411
56586
  const currentModel = await this.readModelStore.validateModel(session.workspace);
56412
- const baseErrors = baseModelBlockingErrors(currentModel);
56587
+ const baseErrors = changeSetBaseModelBlockingErrors(currentModel, draft);
56413
56588
  if (baseErrors.length > 0) {
56414
56589
  throw new FailureError("AC_PRECONDITION_FAILED", "apply-model-invalid", { issueCount: baseErrors.length });
56415
56590
  }
@@ -57684,6 +57859,10 @@ class ArchctxDaemon {
57684
57859
  async completeTaskUnchecked(root, input) {
57685
57860
  this.assertRunning();
57686
57861
  assertNoCallerProvidedAttestationFields(input, "complete-task");
57862
+ if (this.codeFacts.assertCanSync) {
57863
+ root = findRepositoryRoot(root);
57864
+ this.codeFacts.assertCanSync(root);
57865
+ }
57687
57866
  const session = await this.openSession(root);
57688
57867
  let currentHeadSha = input.headSha;
57689
57868
  try {
@@ -58554,6 +58733,7 @@ function completeTaskProjectionFreshness(root) {
58554
58733
  }
58555
58734
 
58556
58735
  // packages/local-runtime/codegraph-adapter/src/index.ts
58736
+ init_src();
58557
58737
  import { execFileSync as execFileSync11 } from "node:child_process";
58558
58738
  import { accessSync as accessSync2, closeSync as closeSync11, constants as fsConstants4, existsSync as existsSync33, openSync as openSync11, readFileSync as readFileSync30, readSync as readSync5, realpathSync as realpathSync12, statSync as statSync10 } from "node:fs";
58559
58739
  import { createRequire as createRequire5 } from "node:module";
@@ -58662,7 +58842,7 @@ function readProjectionCodeGraphStatus2(invocation, root, timeout, envOverrides)
58662
58842
  function projectionCodeGraphStatusIssues2(status, root, phase) {
58663
58843
  const issue = (code, text) => ({
58664
58844
  code,
58665
- message: `CodeGraph ${phase} ${text}`
58845
+ message: phase === "repository" ? CODE_FACTS_ISSUE_MESSAGES[code] : `CodeGraph ${phase} ${text}`
58666
58846
  });
58667
58847
  if (!status.initialized)
58668
58848
  return [issue("not-initialized", "status is not initialized")];
@@ -58738,7 +58918,7 @@ function inspectRepositoryCodeFacts2(root, options = {}) {
58738
58918
  message: (error instanceof Error ? error.message : String(error)).replace(/\s+/g, " ").trim().slice(0, 400)
58739
58919
  };
58740
58920
  }
58741
- const versionIssues = codeGraphVersion === REQUIRED_CODEGRAPH_VERSION2 ? [] : [{ code: "version-mismatch", message: `CodeGraph ${REQUIRED_CODEGRAPH_VERSION2} required, got actual binary ${codeGraphVersion || "unknown"}` }];
58921
+ const versionIssues = codeGraphVersion === REQUIRED_CODEGRAPH_VERSION2 ? [] : [{ code: "version-mismatch", message: CODE_FACTS_ISSUE_MESSAGES["version-mismatch"] }];
58742
58922
  if (!indexPresent) {
58743
58923
  return { schemaVersion, state: "index-missing", indexPresent: false, requiredVersion: REQUIRED_CODEGRAPH_VERSION2, codeGraphVersion, issues: versionIssues };
58744
58924
  }
@@ -58768,7 +58948,7 @@ function inspectRepositoryCodeFacts2(root, options = {}) {
58768
58948
  current: status.index?.currentExtractionVersion ?? null
58769
58949
  },
58770
58950
  indexState: status.index?.state ?? null,
58771
- reindexRecommended: status.index?.reindexRecommended ?? null,
58951
+ reindexRecommended: issues.some((issue) => issue.code !== "pending-source-changes"),
58772
58952
  pendingRefs: status.index?.pendingRefs ?? null,
58773
58953
  pendingChanges: pending ? { added: pending.added, modified: pending.modified, removed: pending.removed } : null,
58774
58954
  issues
@@ -59571,6 +59751,95 @@ function isExternalDocumentationResourceUri(uri) {
59571
59751
  function architectureBookResourceInput(uri) {
59572
59752
  return ARCHITECTURE_BOOK_RESOURCES.find((resource) => resource.uri === uri)?.input;
59573
59753
  }
59754
+ // schemas/repo/architecture-node.schema.json
59755
+ var architecture_node_schema_default2 = {
59756
+ $schema: "https://json-schema.org/draft/2020-12/schema",
59757
+ $id: "https://archctx.repoharness.com/schemas/repo/architecture-node.schema.json",
59758
+ title: "ArchitectureNode",
59759
+ type: "object",
59760
+ additionalProperties: false,
59761
+ required: ["schemaVersion", "id", "kind", "name", "status", "summary"],
59762
+ properties: {
59763
+ schemaVersion: { const: "archcontext.node/v2" },
59764
+ 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-]*)+$" },
59765
+ kind: {
59766
+ type: "string",
59767
+ enum: ["capability", "module", "component", "interface", "event", "datastore", "external-system", "deployment"]
59768
+ },
59769
+ name: { type: "string" },
59770
+ status: { type: "string", enum: ["active", "planned", "deprecated", "removed"] },
59771
+ 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-]*)+$" },
59772
+ summary: { type: "string" },
59773
+ responsibilities: { type: "array", items: { type: "string" }, minItems: 1 },
59774
+ source: {
59775
+ type: "object",
59776
+ additionalProperties: false,
59777
+ properties: {
59778
+ include: { type: "array", items: { type: "string" } },
59779
+ exclude: { type: "array", items: { type: "string" } },
59780
+ entrypoints: {
59781
+ type: "array",
59782
+ items: {
59783
+ type: "object",
59784
+ additionalProperties: false,
59785
+ required: ["id", "path", "symbols"],
59786
+ properties: {
59787
+ id: { type: "string", pattern: "^[a-z][a-z0-9-]*(\\.[a-z0-9][a-z0-9-]*)+$" },
59788
+ path: { type: "string", minLength: 1 },
59789
+ symbols: {
59790
+ type: "array",
59791
+ minItems: 1,
59792
+ items: {
59793
+ type: "object",
59794
+ additionalProperties: false,
59795
+ required: ["name", "sinks"],
59796
+ properties: {
59797
+ name: { type: "string", minLength: 1 },
59798
+ sinks: {
59799
+ type: "array",
59800
+ minItems: 1,
59801
+ items: {
59802
+ type: "object",
59803
+ additionalProperties: false,
59804
+ required: ["id", "path", "symbol"],
59805
+ properties: {
59806
+ id: { type: "string", pattern: "^[a-z][a-z0-9-]*(\\.[a-z0-9][a-z0-9-]*)+$" },
59807
+ path: { type: "string", minLength: 1 },
59808
+ symbol: { type: "string", minLength: 1 }
59809
+ }
59810
+ }
59811
+ }
59812
+ }
59813
+ }
59814
+ }
59815
+ }
59816
+ }
59817
+ }
59818
+ }
59819
+ },
59820
+ ownership: {
59821
+ type: "object",
59822
+ additionalProperties: false,
59823
+ properties: {
59824
+ lifecycle: { type: "array", items: { type: "string" } },
59825
+ data: { type: "array", items: { type: "string" } }
59826
+ }
59827
+ },
59828
+ interfaces: {
59829
+ type: "object",
59830
+ additionalProperties: false,
59831
+ properties: {
59832
+ provides: { type: "array", items: { type: "string" } },
59833
+ consumes: { type: "array", items: { type: "string" } }
59834
+ }
59835
+ },
59836
+ criticality: { type: "string", enum: ["low", "medium", "high", "critical"] },
59837
+ riskDomains: { type: "array", items: { type: "string" } },
59838
+ notes: { type: "array", items: { type: "string" } },
59839
+ extensions: { type: "object", additionalProperties: true }
59840
+ }
59841
+ };
59842
+
59574
59843
  // packages/surfaces/cli/src/main.ts
59575
59844
  var [, , command, ...args] = process.argv;
59576
59845
  var CLI_ENTRY = fileURLToPath2(import.meta.url);
@@ -60244,7 +60513,7 @@ async function runCliUnchecked(command = "help", args = [], cwd, deps = {}) {
60244
60513
  const read = await readResolvePathsFrom(args, cwd, deps);
60245
60514
  if ("problem" in read)
60246
60515
  return errorEnvelope("resolve", "AC_SCHEMA_INVALID", read.problem);
60247
- const model = loadNativeModelFromArchContext(cwd);
60516
+ const model = loadResolveModel(cwd);
60248
60517
  const outcomes = resolveArchitectureOwnersForPaths(model.nodes, read.paths);
60249
60518
  return okEnvelope("resolve", { results: outcomes.map((outcome, index) => resolveResultData(read.paths[index], outcome)) });
60250
60519
  }
@@ -60254,7 +60523,7 @@ async function runCliUnchecked(command = "help", args = [], cwd, deps = {}) {
60254
60523
  if (!isRepoRelativePosixPath(path)) {
60255
60524
  return errorEnvelope("resolve", "AC_SCHEMA_INVALID", "resolve --path must be a repository-relative POSIX path");
60256
60525
  }
60257
- const model = loadNativeModelFromArchContext(cwd);
60526
+ const model = loadResolveModel(cwd);
60258
60527
  return okEnvelope("resolve", resolveResultData(path, resolveArchitectureOwnerForPath(model.nodes, path)));
60259
60528
  }
60260
60529
  case "help":
@@ -63267,6 +63536,37 @@ var CLI_RESOLVE_HELP = {
63267
63536
  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
63537
  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
63538
  };
63539
+ function loadResolveModel(cwd) {
63540
+ if (!existsSync34(join26(cwd, ".archcontext/manifest.yaml"))) {
63541
+ throw new NativeContextError("native-context-model-missing", { path: ".archcontext/manifest.yaml" });
63542
+ }
63543
+ let model;
63544
+ try {
63545
+ model = loadNativeModelFromArchContext(cwd);
63546
+ } catch (error) {
63547
+ const cause = error;
63548
+ if (typeof cause?.code === "string") {
63549
+ throw new NativeContextError("native-context-input-unreadable", {
63550
+ category: "open-or-read",
63551
+ ...typeof cause.path === "string" ? { path: cause.path } : {}
63552
+ });
63553
+ }
63554
+ throw new NativeContextError("native-context-model-invalid", { invariant: "model-parse" });
63555
+ }
63556
+ if (model.nodes.length === 0) {
63557
+ throw new NativeContextError("native-context-model-missing", { category: "no-native-nodes" });
63558
+ }
63559
+ for (const node of model.nodes) {
63560
+ const { issues } = validateJsonSchema(architecture_node_schema_default2, node);
63561
+ if (issues.length > 0) {
63562
+ throw new NativeContextError("native-context-model-invalid", {
63563
+ issues: issues.slice(0, 32).map((issue) => ({ entityId: typeof node.id === "string" ? node.id : "(undeclared)", field: issue.path, invariant: issue.message })),
63564
+ omittedDiagnosticCount: Math.max(0, issues.length - 32)
63565
+ });
63566
+ }
63567
+ }
63568
+ return model;
63569
+ }
63270
63570
  function resolveResultData(path, outcome) {
63271
63571
  if (outcome.status === "matched") {
63272
63572
  const node = outcome.node;
@@ -63579,6 +63879,8 @@ if (__require.main == __require.module) {
63579
63879
  `);
63580
63880
  if (result.ok === false)
63581
63881
  process.exitCode = 1;
63882
+ if (command === "validate" && result.ok && result.data?.valid === false)
63883
+ process.exitCode = 1;
63582
63884
  if (command === "resolve")
63583
63885
  process.exitCode = resolveCommandExitCode(result);
63584
63886
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "archctx",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "Local architecture context CLI for agentic coding workflows.",
5
5
  "private": false,
6
6
  "type": "module",
@@ -0,0 +1,18 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://archctx.repoharness.com/schemas/runtime/native-model-issue.schema.json",
4
+ "title": "NativeModelIssue",
5
+ "description": "A native model schema or reference issue. Source paths are optional for callers that do not supply parsed record paths.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["entityId", "field", "invariant"],
9
+ "properties": {
10
+ "entityId": { "type": "string" },
11
+ "field": { "type": "string" },
12
+ "invariant": { "type": "string" },
13
+ "path": {
14
+ "type": "string",
15
+ "description": "The repository-relative path of the parsed source file that holds this record. It is not derived from the entity ID."
16
+ }
17
+ }
18
+ }