archctx-contracts 0.5.11 → 0.5.13

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,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": "archcontext.capabilities/v1",
3
- "package": { "name": "archctx", "version": "0.5.11" },
3
+ "package": { "name": "archctx", "version": "0.5.13" },
4
4
  "protocols": {
5
5
  "projectionRequest": "archcontext.projection-request/v1",
6
6
  "projectionResult": "archcontext.projection-result/v2",
@@ -2,7 +2,7 @@
2
2
  "schemaVersion": "archcontext.product-version-manifest/v1",
3
3
  "product": {
4
4
  "name": "archctx",
5
- "version": "0.5.11",
5
+ "version": "0.5.13",
6
6
  "distribution": "one-package"
7
7
  },
8
8
  "packageManager": "bun@1.4.0",
@@ -12,25 +12,25 @@
12
12
  "surfaces": {
13
13
  "cli": {
14
14
  "packageName": "@archcontext/surfaces",
15
- "version": "0.5.11",
15
+ "version": "0.5.13",
16
16
  "entrypoint": "archctx"
17
17
  },
18
18
  "daemon": {
19
19
  "packageName": "@archcontext/local-runtime",
20
- "version": "0.5.11",
20
+ "version": "0.5.13",
21
21
  "entrypoint": "archctx daemon start",
22
22
  "rpcSchemaVersion": "archcontext.runtime-rpc/v1"
23
23
  },
24
24
  "mcp": {
25
25
  "packageName": "@archcontext/surfaces",
26
- "version": "0.5.11",
26
+ "version": "0.5.13",
27
27
  "entrypoint": "archctx mcp",
28
28
  "transport": "stdio"
29
29
  }
30
30
  },
31
31
  "schemas": {
32
32
  "schemaSetVersion": "2026-06-25.al0-ledger",
33
- "contractsPackageVersion": "0.5.11",
33
+ "contractsPackageVersion": "0.5.13",
34
34
  "reviewChallenge": "archcontext.review-challenge/v2",
35
35
  "attestation": "archcontext.attestation/v2",
36
36
  "deviceIdentity": "archcontext.device-identity/v1",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "archctx-contracts",
3
- "version": "0.5.11",
3
+ "version": "0.5.13",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -4,62 +4,235 @@
4
4
  "title": "ChangeSet",
5
5
  "type": "object",
6
6
  "additionalProperties": false,
7
- "required": ["schemaVersion", "id", "status", "base", "reason", "operations", "preconditions", "postconditions", "requiresConfirmation", "idempotencyKey"],
7
+ "required": [
8
+ "schemaVersion",
9
+ "id",
10
+ "status",
11
+ "base",
12
+ "reason",
13
+ "operations",
14
+ "preconditions",
15
+ "postconditions",
16
+ "requiresConfirmation",
17
+ "idempotencyKey"
18
+ ],
8
19
  "properties": {
9
- "schemaVersion": { "const": "archcontext.changeset/v1" },
10
- "id": { "type": "string", "pattern": "^changeset\\.[a-zA-Z0-9_-]+$" },
11
- "status": { "type": "string", "enum": ["proposed", "approved", "applied", "rolled-back", "rejected"] },
20
+ "schemaVersion": {
21
+ "const": "archcontext.changeset/v1"
22
+ },
23
+ "id": {
24
+ "type": "string",
25
+ "pattern": "^changeset\\.[a-zA-Z0-9_-]+$"
26
+ },
27
+ "status": {
28
+ "type": "string",
29
+ "enum": [
30
+ "proposed",
31
+ "approved",
32
+ "applied",
33
+ "rolled-back",
34
+ "rejected"
35
+ ]
36
+ },
12
37
  "base": {
13
38
  "type": "object",
14
39
  "additionalProperties": false,
15
- "required": ["headSha", "worktreeDigest", "modelDigest"],
40
+ "required": [
41
+ "headSha",
42
+ "worktreeDigest",
43
+ "modelDigest"
44
+ ],
16
45
  "properties": {
17
- "headSha": { "type": "string" },
18
- "worktreeDigest": { "type": "string", "pattern": "^sha256:[a-f0-9]{64}$" },
19
- "modelDigest": { "type": "string", "pattern": "^sha256:[a-f0-9]{64}$" }
46
+ "headSha": {
47
+ "type": "string"
48
+ },
49
+ "worktreeDigest": {
50
+ "type": "string",
51
+ "pattern": "^sha256:[a-f0-9]{64}$"
52
+ },
53
+ "modelDigest": {
54
+ "type": "string",
55
+ "pattern": "^sha256:[a-f0-9]{64}$"
56
+ }
20
57
  }
21
58
  },
22
59
  "reason": {
23
60
  "type": "object",
24
61
  "additionalProperties": false,
25
- "required": ["taskSessionId"],
62
+ "required": [
63
+ "taskSessionId"
64
+ ],
26
65
  "properties": {
27
- "taskSessionId": { "type": "string" },
28
- "interventionId": { "type": "string" }
66
+ "taskSessionId": {
67
+ "type": "string"
68
+ },
69
+ "interventionId": {
70
+ "type": "string"
71
+ }
29
72
  }
30
73
  },
31
74
  "operations": {
32
75
  "type": "array",
33
76
  "minItems": 1,
34
77
  "items": {
35
- "type": "object",
36
- "additionalProperties": true,
37
- "required": ["op", "expectedHash"],
38
- "properties": {
39
- "op": { "type": "string", "enum": ["create_entity", "update_entity_fields", "delete_entity", "write_policy", "write_waiver", "render_projection", "render_agent_context"] },
40
- "entityId": { "type": "string" },
41
- "path": { "type": "string" },
42
- "expectedHash": { "type": "string" },
43
- "projectionFiles": {
44
- "type": "array",
45
- "items": {
46
- "type": "object",
47
- "additionalProperties": false,
48
- "required": ["path", "expectedHash", "body"],
49
- "properties": {
50
- "path": { "type": "string" },
51
- "expectedHash": { "type": "string" },
52
- "body": { "type": "string" }
78
+ "oneOf": [
79
+ {
80
+ "type": "object",
81
+ "additionalProperties": true,
82
+ "required": [
83
+ "op",
84
+ "expectedHash"
85
+ ],
86
+ "properties": {
87
+ "op": {
88
+ "type": "string",
89
+ "enum": [
90
+ "create_entity",
91
+ "update_entity_fields",
92
+ "delete_entity",
93
+ "write_policy",
94
+ "write_waiver",
95
+ "render_projection",
96
+ "render_agent_context"
97
+ ]
98
+ },
99
+ "entityId": {
100
+ "type": "string"
101
+ },
102
+ "path": {
103
+ "type": "string"
104
+ },
105
+ "expectedHash": {
106
+ "type": "string"
107
+ },
108
+ "projectionFiles": {
109
+ "type": "array",
110
+ "items": {
111
+ "type": "object",
112
+ "additionalProperties": false,
113
+ "required": [
114
+ "path",
115
+ "expectedHash",
116
+ "body"
117
+ ],
118
+ "properties": {
119
+ "path": {
120
+ "type": "string"
121
+ },
122
+ "expectedHash": {
123
+ "type": "string"
124
+ },
125
+ "body": {
126
+ "type": "string"
127
+ }
128
+ }
129
+ }
130
+ }
131
+ }
132
+ },
133
+ {
134
+ "type": "object",
135
+ "additionalProperties": false,
136
+ "required": [
137
+ "op",
138
+ "path",
139
+ "expectedHash",
140
+ "fields"
141
+ ],
142
+ "properties": {
143
+ "op": {
144
+ "const": "update_manifest_fields"
145
+ },
146
+ "path": {
147
+ "const": ".archcontext/manifest.yaml"
148
+ },
149
+ "expectedHash": {
150
+ "type": "string",
151
+ "pattern": "^sha256:[a-f0-9]{64}$"
152
+ },
153
+ "fields": {
154
+ "type": "object",
155
+ "additionalProperties": false,
156
+ "required": [
157
+ "content.decisions"
158
+ ],
159
+ "properties": {
160
+ "content.decisions": {
161
+ "const": "docs/adr"
162
+ }
163
+ }
164
+ }
165
+ }
166
+ },
167
+ {
168
+ "type": "object",
169
+ "additionalProperties": false,
170
+ "required": [
171
+ "op",
172
+ "path",
173
+ "expectedHash",
174
+ "references"
175
+ ],
176
+ "properties": {
177
+ "op": {
178
+ "const": "update_adr_references"
179
+ },
180
+ "path": {
181
+ "type": "string",
182
+ "pattern": "^docs/adr/ADR-[0-9]{4}-[a-z0-9][a-z0-9-]*\\.md$"
183
+ },
184
+ "expectedHash": {
185
+ "type": "string",
186
+ "pattern": "^sha256:[a-f0-9]{64}$"
187
+ },
188
+ "references": {
189
+ "type": "array",
190
+ "minItems": 1,
191
+ "items": {
192
+ "type": "object",
193
+ "additionalProperties": false,
194
+ "required": [
195
+ "from",
196
+ "to"
197
+ ],
198
+ "properties": {
199
+ "from": {
200
+ "type": "string",
201
+ "pattern": "^[a-z0-9][a-z0-9._-]{2,127}$"
202
+ },
203
+ "to": {
204
+ "type": "string",
205
+ "pattern": "^[a-z0-9][a-z0-9._-]{2,127}$"
206
+ }
207
+ }
208
+ }
53
209
  }
54
210
  }
55
211
  }
56
- }
212
+ ]
57
213
  }
58
214
  },
59
- "preconditions": { "type": "array", "items": { "type": "string" } },
60
- "postconditions": { "type": "array", "items": { "type": "string" } },
61
- "requiresConfirmation": { "type": "boolean" },
62
- "idempotencyKey": { "type": "string" },
63
- "extensions": { "type": "object", "additionalProperties": true }
215
+ "preconditions": {
216
+ "type": "array",
217
+ "items": {
218
+ "type": "string"
219
+ }
220
+ },
221
+ "postconditions": {
222
+ "type": "array",
223
+ "items": {
224
+ "type": "string"
225
+ }
226
+ },
227
+ "requiresConfirmation": {
228
+ "type": "boolean"
229
+ },
230
+ "idempotencyKey": {
231
+ "type": "string"
232
+ },
233
+ "extensions": {
234
+ "type": "object",
235
+ "additionalProperties": true
236
+ }
64
237
  }
65
238
  }
@@ -71,3 +71,82 @@ export interface ArchitectureNotApplicableFlowV1 extends ArchitectureFlowBaseV1
71
71
  applicability: "not-applicable";
72
72
  rationale: string;
73
73
  }
74
+
75
+ /**
76
+ * A `forbid-dependency` constraint (`archcontext.constraint/v1`), narrowed to the fields the
77
+ * dependency gate evaluates. A file owned by a `scope.nodes` node (or a descendant) must not
78
+ * import a file owned by a `rule.targets` node (or a descendant). v1 has no `allowedVia` escape.
79
+ */
80
+ export interface DependencyConstraintV1 {
81
+ id: string;
82
+ severity: "error" | "warning";
83
+ scope: { nodes: string[] };
84
+ rule: { type: "forbid-dependency"; targets: string[] };
85
+ rationale: string;
86
+ }
87
+
88
+ export const DEPENDENCY_CONSTRAINT_STATUSES = ["not-applicable", "pass", "undetermined", "violated"] as const;
89
+ export type DependencyConstraintStatus = (typeof DEPENDENCY_CONSTRAINT_STATUSES)[number];
90
+
91
+ /**
92
+ * Why a dependency result is not a complete observation: no index answered, the index attested
93
+ * to a different worktree, the import dump hit its limit, a constrained file carries a
94
+ * repository-local import that resolved to no file, or the workspace package map could not be
95
+ * read, so no bare workspace specifier could be resolved.
96
+ */
97
+ export const DEPENDENCY_CONSTRAINT_REASON_CODES = [
98
+ "code-facts-stale",
99
+ "code-facts-truncated",
100
+ "code-facts-unavailable",
101
+ "unresolved-import",
102
+ "workspace-resolution-failed"
103
+ ] as const;
104
+ export type DependencyConstraintReasonCode = (typeof DEPENDENCY_CONSTRAINT_REASON_CODES)[number];
105
+
106
+ export interface DependencyConstraintViolationV1 {
107
+ constraintId: string;
108
+ fromPath: string;
109
+ toPath: string;
110
+ fromNode: string;
111
+ toNode: string;
112
+ severity: "error" | "warning";
113
+ }
114
+
115
+ export interface DependencyConstraintEvaluationV1 {
116
+ schemaVersion: "archcontext.dependency-constraint-evaluation/v1";
117
+ status: DependencyConstraintStatus;
118
+ /** `complete` only when an index attested to the evaluated worktree and was not truncated. */
119
+ coverage: "complete" | "partial" | "unknown";
120
+ reasonCodes: DependencyConstraintReasonCode[];
121
+ constraintIds: string[];
122
+ /** Import observations the evaluation read; zero when the index did not attest to this tree. */
123
+ importEdgeCount: number;
124
+ /** Repository-local specifiers from constrained files that resolved to no file. */
125
+ unresolvedImports: { from: string; specifier: string }[];
126
+ violations: DependencyConstraintViolationV1[];
127
+ }
128
+
129
+ /**
130
+ * The closed `failOn` vocabulary of `.archcontext/policies/review.yaml`. A review finding in one
131
+ * of these categories blocks only when its category is listed; findings outside the vocabulary
132
+ * are never affected by the policy.
133
+ */
134
+ export const REVIEW_FAIL_ON_CATEGORIES = [
135
+ "incomplete-intervention",
136
+ "invalid-schema",
137
+ "prohibited-dependency",
138
+ "stale-context",
139
+ "unjustified-compatibility"
140
+ ] as const;
141
+ export type ReviewFailOnCategory = (typeof REVIEW_FAIL_ON_CATEGORIES)[number];
142
+
143
+ /**
144
+ * The effective review policy. An error in a category missing from `failOn` is downgraded to a
145
+ * warning, with one exception: the task-snapshot HEAD-mismatch `stale-context` finding always stays
146
+ * an error, because a stale snapshot skips every other review gate.
147
+ */
148
+ export interface ReviewPolicyV1 {
149
+ failOn: ReviewFailOnCategory[];
150
+ /** `default` when the policy file is missing or invalid in any way: every category blocks. */
151
+ source: "policy-file" | "default";
152
+ }
package/src/ports.ts CHANGED
@@ -140,13 +140,38 @@ export interface LocalStorePort {
140
140
  saveReviewResult(reviewId: string, result: unknown): Promise<void>;
141
141
  }
142
142
 
143
+ export interface ModelValidationResult {
144
+ valid: boolean;
145
+ errors: string[];
146
+ /**
147
+ * The subset of `errors` that are cross-file references resolving to nothing, such as an ADR
148
+ * `appliesTo` id with no node. Omitted when empty.
149
+ */
150
+ referenceErrors?: string[];
151
+ /** Problems that do not invalidate the model, such as an ignored legacy setting. Omitted when empty. */
152
+ warnings?: string[];
153
+ modelDigest: string;
154
+ }
155
+
143
156
  export interface ModelStorePort {
144
157
  loadManifest(workspace: WorkspaceRef): Promise<unknown>;
145
158
  loadModel(workspace: WorkspaceRef): Promise<unknown[]>;
146
- validateModel(workspace: WorkspaceRef): Promise<{ valid: boolean; errors: string[]; modelDigest: string }>;
159
+ validateModel(workspace: WorkspaceRef): Promise<ModelValidationResult>;
147
160
  writeChangeSetPreview(changeSet: unknown): Promise<{ digest: string; summary: string }>;
148
161
  }
149
162
 
163
+ /**
164
+ * Errors that make a model unusable as the base of a write. Dangling references are excluded so
165
+ * that a change repairing them (for example restoring a deleted node) is not blocked; the
166
+ * after-apply model is still validated in full.
167
+ */
168
+ export function baseModelBlockingErrors(result: ModelValidationResult): string[] {
169
+ if (result.valid) return [];
170
+ if (result.errors.length === 0) return ["unknown validation error"];
171
+ const references = new Set(result.referenceErrors ?? []);
172
+ return result.errors.filter((error) => !references.has(error));
173
+ }
174
+
150
175
  export interface PolicyPort {
151
176
  evaluateChangeSet(changeSet: unknown): Promise<{ allowed: boolean; violations: string[] }>;
152
177
  evaluateReview(input: unknown): Promise<{ result: "pass" | "fail"; findings: unknown[] }>;
@@ -750,3 +775,66 @@ export interface ChatGptGaToolContract {
750
775
  requiresLocalConfirmationForWrite: boolean;
751
776
  disclosure: string;
752
777
  }
778
+
779
+ export type DetachedReviewWorktreeReason =
780
+ | "HEAD_UNAVAILABLE"
781
+ | "HEAD_SHA_MISMATCH"
782
+ | "TREE_OID_MISMATCH"
783
+ | "WORKTREE_NOT_DETACHED"
784
+ | "WORKTREE_NOT_CLEAN";
785
+
786
+ export interface DetachedReviewWorktreeVerification {
787
+ schemaVersion: "archcontext.detached-review-worktree-verification/v1";
788
+ accepted: boolean;
789
+ reasonCode?: DetachedReviewWorktreeReason;
790
+ expected: {
791
+ headSha: string;
792
+ headTreeOid?: string;
793
+ };
794
+ observed: {
795
+ headSha?: string;
796
+ headTreeOid?: string;
797
+ detached?: boolean;
798
+ clean?: boolean;
799
+ };
800
+ }
801
+
802
+
803
+ export interface ReviewCheckoutGitPort {
804
+ findRepositoryRoot(start: string): string;
805
+ readOriginUrl(root: string): string | null;
806
+ verifyDetachedWorktree(input: {
807
+ worktreeRoot: string;
808
+ expectedHeadSha: string;
809
+ expectedHeadTreeOid?: string;
810
+ }): DetachedReviewWorktreeVerification;
811
+ }
812
+
813
+ export interface NonLocalEgressChannel {
814
+ channel: "context7" | "agent-audit" | "github-issue-publishing" | "npm-update-check" | "codegraph-telemetry";
815
+ destination: string;
816
+ trigger: string;
817
+ data: string;
818
+ status: "enabled" | "declared-awaiting-user-consent" | "blocked-by-policy";
819
+ }
820
+
821
+ export interface LocalEgressReport {
822
+ ok: boolean;
823
+ policyMode: "configured" | "local-only";
824
+ enforcement: "application-admission";
825
+ defaultOutbound: "local-only";
826
+ effectiveOutbound: "local-only" | "non-local";
827
+ nonLocalEgress: NonLocalEgressChannel[];
828
+ cloudContentUpload: "deny";
829
+ secureMcpTunnel: "disabled-by-default";
830
+ thirdPartyTelemetry: "disabled" | "not-disabled-by-env";
831
+ codeGraph: {
832
+ provider: "codegraph";
833
+ telemetry: "disabled" | "not-disabled-by-env";
834
+ envVar: string;
835
+ configuredValue: string | null;
836
+ effectiveValue: string;
837
+ source: "archcontext-default" | "environment";
838
+ };
839
+ warnings: string[];
840
+ }
@@ -1,5 +1,5 @@
1
1
  export const ARCHCONTEXT_PRODUCT_NAME = "archctx";
2
- export const ARCHCONTEXT_PRODUCT_VERSION = "0.5.11";
2
+ export const ARCHCONTEXT_PRODUCT_VERSION = "0.5.13";
3
3
  export const ARCHCONTEXT_PACKAGE_MANAGER = "bun@1.4.0";
4
4
  export const ARCHCONTEXT_NODE_RANGE = ">=22.22 <26";
5
5
  export const LOCAL_RUNTIME_RPC_SCHEMA_VERSION = "archcontext.runtime-rpc/v1";