frontend-project-context 1.2.0 → 1.3.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.
@@ -0,0 +1,55 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "urn:frontend-project-context:schema:stage-receipt:1",
4
+ "title": "Frontend Project Context Stage Receipt",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["schemaVersion", "kind", "projectId", "taskId", "stageId", "planDigest", "inputBundleDigest", "status", "changedPaths", "acceptanceResults", "verificationResults", "decisions", "openIssues"],
8
+ "properties": {
9
+ "schemaVersion": { "const": 1 },
10
+ "kind": { "const": "stage-receipt" },
11
+ "projectId": { "$ref": "#/$defs/id" },
12
+ "taskId": { "$ref": "#/$defs/id" },
13
+ "stageId": { "$ref": "#/$defs/id" },
14
+ "planDigest": { "$ref": "#/$defs/digest" },
15
+ "inputBundleDigest": { "$ref": "#/$defs/digest" },
16
+ "status": { "enum": ["completed", "blocked"] },
17
+ "changedPaths": { "$ref": "#/$defs/paths" },
18
+ "acceptanceResults": {
19
+ "type": "array",
20
+ "items": {
21
+ "type": "object",
22
+ "additionalProperties": false,
23
+ "required": ["id", "status", "evidence"],
24
+ "properties": {
25
+ "id": { "$ref": "#/$defs/id" },
26
+ "status": { "enum": ["observed", "not-observed"] },
27
+ "evidence": { "$ref": "#/$defs/paths" }
28
+ }
29
+ }
30
+ },
31
+ "verificationResults": {
32
+ "type": "array",
33
+ "items": {
34
+ "type": "object",
35
+ "additionalProperties": false,
36
+ "required": ["id", "status", "summary", "evidence"],
37
+ "properties": {
38
+ "id": { "$ref": "#/$defs/id" },
39
+ "status": { "enum": ["passed", "failed", "observed", "skipped"] },
40
+ "summary": { "type": "string", "minLength": 1 },
41
+ "evidence": { "$ref": "#/$defs/paths" }
42
+ }
43
+ }
44
+ },
45
+ "decisions": { "$ref": "#/$defs/texts" },
46
+ "openIssues": { "$ref": "#/$defs/texts" },
47
+ "nextStageId": { "$ref": "#/$defs/id" }
48
+ },
49
+ "$defs": {
50
+ "id": { "type": "string", "pattern": "^[a-z0-9]+(?:[.-][a-z0-9]+)*$" },
51
+ "digest": { "type": "string", "pattern": "^sha256:[a-f0-9]{64}$" },
52
+ "paths": { "type": "array", "uniqueItems": true, "items": { "type": "string", "minLength": 1 } },
53
+ "texts": { "type": "array", "uniqueItems": true, "items": { "type": "string", "minLength": 1 } }
54
+ }
55
+ }
@@ -0,0 +1,85 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "urn:frontend-project-context:schema:task-context-plan:1",
4
+ "title": "Frontend Project Context Task Context Plan",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["schemaVersion", "kind", "projectId", "task", "workspace", "snapshots", "budget", "stages"],
8
+ "properties": {
9
+ "schemaVersion": { "const": 1 },
10
+ "kind": { "const": "task-context-plan" },
11
+ "projectId": { "$ref": "#/$defs/id" },
12
+ "task": {
13
+ "type": "object",
14
+ "additionalProperties": false,
15
+ "required": ["id", "title", "goal", "acceptance"],
16
+ "properties": {
17
+ "id": { "$ref": "#/$defs/id" },
18
+ "title": { "$ref": "#/$defs/text" },
19
+ "goal": { "$ref": "#/$defs/text" },
20
+ "acceptance": {
21
+ "type": "array",
22
+ "minItems": 1,
23
+ "items": {
24
+ "type": "object",
25
+ "additionalProperties": false,
26
+ "required": ["id", "text"],
27
+ "properties": { "id": { "$ref": "#/$defs/id" }, "text": { "$ref": "#/$defs/text" } }
28
+ }
29
+ }
30
+ }
31
+ },
32
+ "workspace": {
33
+ "type": "object",
34
+ "additionalProperties": false,
35
+ "required": ["branchLabel", "baseRevision"],
36
+ "properties": { "branchLabel": { "$ref": "#/$defs/text" }, "baseRevision": { "$ref": "#/$defs/text" } }
37
+ },
38
+ "snapshots": { "$ref": "#/$defs/snapshots" },
39
+ "budget": {
40
+ "type": "object",
41
+ "additionalProperties": false,
42
+ "required": ["maxUtf8Bytes", "maxReadTargets"],
43
+ "properties": {
44
+ "maxUtf8Bytes": { "type": "integer", "minimum": 1 },
45
+ "maxReadTargets": { "type": "integer", "minimum": 1 }
46
+ }
47
+ },
48
+ "stages": {
49
+ "type": "array",
50
+ "minItems": 1,
51
+ "items": {
52
+ "type": "object",
53
+ "additionalProperties": false,
54
+ "required": ["id", "title", "objective", "dependsOn", "paths", "acceptanceIds"],
55
+ "properties": {
56
+ "id": { "$ref": "#/$defs/id" },
57
+ "title": { "$ref": "#/$defs/text" },
58
+ "objective": { "$ref": "#/$defs/text" },
59
+ "dependsOn": { "$ref": "#/$defs/ids" },
60
+ "paths": { "$ref": "#/$defs/paths" },
61
+ "acceptanceIds": { "$ref": "#/$defs/nonEmptyIds" }
62
+ }
63
+ }
64
+ }
65
+ },
66
+ "$defs": {
67
+ "id": { "type": "string", "pattern": "^[a-z0-9]+(?:[.-][a-z0-9]+)*$" },
68
+ "text": { "type": "string", "minLength": 1 },
69
+ "digest": { "type": "string", "pattern": "^sha256:[a-f0-9]{64}$" },
70
+ "path": { "type": "string", "minLength": 1 },
71
+ "ids": { "type": "array", "uniqueItems": true, "items": { "$ref": "#/$defs/id" } },
72
+ "nonEmptyIds": { "type": "array", "minItems": 1, "uniqueItems": true, "items": { "$ref": "#/$defs/id" } },
73
+ "paths": { "type": "array", "minItems": 1, "uniqueItems": true, "items": { "$ref": "#/$defs/path" } },
74
+ "snapshots": {
75
+ "type": "object",
76
+ "additionalProperties": false,
77
+ "required": ["contract", "sourcesLock", "projectionsLock"],
78
+ "properties": {
79
+ "contract": { "$ref": "#/$defs/digest" },
80
+ "sourcesLock": { "$ref": "#/$defs/digest" },
81
+ "projectionsLock": { "$ref": "#/$defs/digest" }
82
+ }
83
+ }
84
+ }
85
+ }
@@ -0,0 +1,74 @@
1
+ import { ASSIST_BUNDLE_SCHEMA_VERSION } from "./assist.mjs";
2
+ import { DASHBOARD_SCHEMA_VERSION } from "./dashboard-model.mjs";
3
+ import {
4
+ ACTION_KINDS,
5
+ ACTION_PLAN_SCHEMA_VERSION,
6
+ CAPABILITIES_SCHEMA_VERSION,
7
+ COMMANDS,
8
+ EXCHANGE_PROTOCOL_VERSION,
9
+ PACKAGE_VERSION,
10
+ REVIEW_BUNDLE_SCHEMA_VERSION,
11
+ } from "./exchange-schema.mjs";
12
+ import { inspectProjectInitialization, loadProject } from "./project-store.mjs";
13
+ import { RENDERER_VERSION } from "./renderer.mjs";
14
+ import {
15
+ CONTEXT_BUDGET_UNIT,
16
+ INTEGRATION_REVIEW_BUNDLE_SCHEMA_VERSION,
17
+ STAGE_CONTEXT_BUNDLE_SCHEMA_VERSION,
18
+ STAGE_RECEIPT_SCHEMA_VERSION,
19
+ TASK_CONTEXT_PLAN_SCHEMA_VERSION,
20
+ } from "./task-context-schema.mjs";
21
+
22
+ function schemas() {
23
+ return {
24
+ actionPlan: ACTION_PLAN_SCHEMA_VERSION,
25
+ assistBundle: ASSIST_BUNDLE_SCHEMA_VERSION,
26
+ capabilities: CAPABILITIES_SCHEMA_VERSION,
27
+ contract: 2,
28
+ dashboardViewModel: DASHBOARD_SCHEMA_VERSION,
29
+ integrationReviewBundle: INTEGRATION_REVIEW_BUNDLE_SCHEMA_VERSION,
30
+ projectionLock: 1,
31
+ projectionRenderer: RENDERER_VERSION,
32
+ proposal: 1,
33
+ reviewBundle: REVIEW_BUNDLE_SCHEMA_VERSION,
34
+ sourceLock: 1,
35
+ stageContextBundle: STAGE_CONTEXT_BUNDLE_SCHEMA_VERSION,
36
+ stageReceipt: STAGE_RECEIPT_SCHEMA_VERSION,
37
+ taskContextPlan: TASK_CONTEXT_PLAN_SCHEMA_VERSION,
38
+ };
39
+ }
40
+
41
+ export async function buildCapabilities(root) {
42
+ const initialization = await inspectProjectInitialization(root);
43
+ let project = null;
44
+ if (initialization.status === "initialized") {
45
+ const loaded = await loadProject(root);
46
+ project = { id: loaded.contract.project.id, name: loaded.contract.project.name };
47
+ }
48
+ return {
49
+ schemaVersion: CAPABILITIES_SCHEMA_VERSION,
50
+ package: { name: "frontend-project-context", version: PACKAGE_VERSION },
51
+ exchangeProtocolVersion: EXCHANGE_PROTOCOL_VERSION,
52
+ schemas: schemas(),
53
+ commands: [...COMMANDS],
54
+ actionKinds: [...ACTION_KINDS],
55
+ contextBudget: { unit: CONTEXT_BUDGET_UNIT, modelTokens: false, callerMustProvideLimit: true },
56
+ initialization: initialization.status,
57
+ initialized: initialization.status === "initialized",
58
+ project,
59
+ boundaries: {
60
+ provider: false,
61
+ agentRuntime: false,
62
+ git: false,
63
+ network: false,
64
+ dependencyInstallation: false,
65
+ automaticApproval: false,
66
+ businessCodeWrites: false,
67
+ taskExecution: false,
68
+ stagePathBodyReads: false,
69
+ applyPlan: false,
70
+ scheduler: false,
71
+ daemon: false,
72
+ },
73
+ };
74
+ }
@@ -16,6 +16,7 @@ import { normalizeRelativePath, resolveExistingInside, resolveProjectRoot, resol
16
16
  import { initializeProject, inspectProjectInitialization, loadProject } from "./project-store.mjs";
17
17
  import { publishProjection } from "./projection-store.mjs";
18
18
  import { renderContextBundle } from "./renderer.mjs";
19
+ import { buildIntegrationReviewBundleFiles, buildStageContextBundleFiles } from "./task-context.mjs";
19
20
 
20
21
  const HELP = `project-context — model-neutral project contract compiler
21
22
 
@@ -38,6 +39,8 @@ Usage:
38
39
  project-context dashboard --project PATH [--json]
39
40
  project-context sync --project PATH [--changed-path RELATIVE_PATH...] [--json]
40
41
  project-context preflight --project PATH --plan FILE [--json]
42
+ project-context stage-context --project PATH --plan FILE --stage STAGE_ID [--receipt FILE...] [--changed-path RELATIVE_PATH...] [--json]
43
+ project-context integration-review --project PATH --plan FILE [--receipt FILE...] [--main-changed-path RELATIVE_PATH...] [--branch-changed-path RELATIVE_PATH...] [--json]
41
44
 
42
45
  All commands are read-only unless their own --write flag is present.
43
46
  `;
@@ -45,9 +48,11 @@ const VALUE_FLAGS = new Set([
45
48
  "project", "id", "name", "output", "proposal", "by", "task", "target", "rationale",
46
49
  "kind", "pointer", "reference", "subject", "value", "value-json", "statement", "scope", "scope-path",
47
50
  "verification", "verification-source", "verification-expected-json",
48
- "expected-digest", "expected-item-digest", "expected-source-digest", "locale", "plan",
51
+ "expected-digest", "expected-item-digest", "expected-source-digest", "locale", "plan", "stage",
52
+ ]);
53
+ const LIST_FLAGS = new Set([
54
+ "ids", "path", "changed-path", "sources", "overrides", "affected-items", "receipt", "main-changed-path", "branch-changed-path",
49
55
  ]);
50
- const LIST_FLAGS = new Set(["ids", "path", "changed-path", "sources", "overrides", "affected-items"]);
51
56
  const BOOLEAN_FLAGS = new Set(["write", "json", "full-json", "help", "pending"]);
52
57
  const COMMAND_OPTIONS = new Map([
53
58
  ["init", new Set(["project", "id", "name", "write", "json", "help"])],
@@ -81,6 +86,8 @@ const COMMAND_OPTIONS = new Map([
81
86
  ["dashboard", new Set(["project", "json", "help"])],
82
87
  ["sync", new Set(["project", "changed-path", "json", "help"])],
83
88
  ["preflight", new Set(["project", "plan", "json", "help"])],
89
+ ["stage-context", new Set(["project", "plan", "stage", "receipt", "changed-path", "json", "help"])],
90
+ ["integration-review", new Set(["project", "plan", "receipt", "main-changed-path", "branch-changed-path", "json", "help"])],
84
91
  ]);
85
92
 
86
93
  export function parseArgs(argv) {
@@ -330,6 +337,24 @@ async function runCommand(command, options) {
330
337
  const summary = `Preflight ${bundle.status}: ${bundle.summary.reviewable} reviewable, ${bundle.summary.blocked} blocked action(s).\n`;
331
338
  return { exitCode: bundle.status === "reviewable" ? 0 : 1, stdout: jsonOrText(options, bundle, summary), stderr: "" };
332
339
  }
340
+ if (command === "stage-context") {
341
+ const bundle = await buildStageContextBundleFiles(root, required(options, "plan"), {
342
+ stageId: required(options, "stage"),
343
+ receiptPaths: options.receipt ?? [],
344
+ changedPaths: options["changed-path"] ?? [],
345
+ });
346
+ const summary = `Stage context ${bundle.status}: ${bundle.stage.id}; ${bundle.contractItems.length} contract item(s), ${bundle.readTargets.length} read target(s), ${bundle.budget.usedUtf8Bytes}/${bundle.budget.maxUtf8Bytes} UTF-8 bytes.\n`;
347
+ return { exitCode: bundle.status === "ready" ? 0 : 1, stdout: jsonOrText(options, bundle, summary), stderr: "" };
348
+ }
349
+ if (command === "integration-review") {
350
+ const bundle = await buildIntegrationReviewBundleFiles(root, required(options, "plan"), {
351
+ receiptPaths: options.receipt ?? [],
352
+ mainChangedPaths: options["main-changed-path"] ?? [],
353
+ branchChangedPaths: options["branch-changed-path"] ?? [],
354
+ });
355
+ const summary = `Integration review ${bundle.status}: ${bundle.findings.length} finding(s), ${bundle.contractOverlapItemIds.length} contract overlap(s), ${bundle.decisionCandidates.length} decision candidate(s).\n`;
356
+ return { exitCode: bundle.status === "reviewable" ? 0 : 1, stdout: jsonOrText(options, bundle, summary), stderr: "" };
357
+ }
333
358
  const project = await loadProject(root);
334
359
  if (command === "register") {
335
360
  const result = await registerSource(root, project, {
@@ -2,7 +2,7 @@ import { canonicalValue, digestJson, validateJsonValue } from "./canonical-json.
2
2
  import { fail } from "./errors.mjs";
3
3
  import { normalizeRelativePath } from "./path-policy.mjs";
4
4
 
5
- export const PACKAGE_VERSION = "1.2.0";
5
+ export const PACKAGE_VERSION = "1.3.0";
6
6
  export const EXCHANGE_PROTOCOL_VERSION = 1;
7
7
  export const ACTION_PLAN_SCHEMA_VERSION = 1;
8
8
  export const REVIEW_BUNDLE_SCHEMA_VERSION = 1;
@@ -21,8 +21,8 @@ export const ACTION_KINDS = Object.freeze([
21
21
 
22
22
  export const COMMANDS = Object.freeze([
23
23
  "accept-source-change", "approve", "capabilities", "check", "context", "dashboard", "deprecate",
24
- "deprecate-source", "discover", "init", "preflight", "propose", "publish", "register", "review-source",
25
- "revise", "setup", "sync",
24
+ "deprecate-source", "discover", "init", "integration-review", "preflight", "propose", "publish", "register",
25
+ "review-source", "revise", "setup", "stage-context", "sync",
26
26
  ]);
27
27
 
28
28
  const ACTION_KIND_SET = new Set(ACTION_KINDS);
@@ -3,17 +3,10 @@ import { approvePendingItems, approveProposal } from "./approver.mjs";
3
3
  import { registerSource, buildItemProposal } from "./authoring.mjs";
4
4
  import { blockingContextFindings, checkProject } from "./checker.mjs";
5
5
  import { canonicalJson, digestJson, prettyCanonicalJson, sha256 } from "./canonical-json.mjs";
6
- import { DASHBOARD_SCHEMA_VERSION } from "./dashboard-model.mjs";
7
6
  import { ProjectContextError, fail } from "./errors.mjs";
8
7
  import {
9
- ACTION_KINDS,
10
- ACTION_PLAN_SCHEMA_VERSION,
11
8
  assertActionPlanConflictFree,
12
- CAPABILITIES_SCHEMA_VERSION,
13
- COMMANDS,
14
- EXCHANGE_PROTOCOL_VERSION,
15
9
  itemPrimitiveInput,
16
- PACKAGE_VERSION,
17
10
  REVIEW_BUNDLE_SCHEMA_VERSION,
18
11
  validateActionPlan,
19
12
  validateReviewBundle,
@@ -21,17 +14,12 @@ import {
21
14
  import { readJsonFile } from "./io.mjs";
22
15
  import { acceptSourceChange, deprecateItem, deprecateSource, reviewSource, reviseItem } from "./maintenance.mjs";
23
16
  import { resolveExistingInside, resolveWritableInside } from "./path-policy.mjs";
24
- import { inspectProjectInitialization, loadProject } from "./project-store.mjs";
17
+ import { loadProject } from "./project-store.mjs";
25
18
  import { publishProjection } from "./projection-store.mjs";
26
- import { RENDERER_VERSION } from "./renderer.mjs";
27
- import { ASSIST_BUNDLE_SCHEMA_VERSION } from "./assist.mjs";
19
+ export { buildCapabilities } from "./capabilities.mjs";
28
20
 
29
21
  const INTERNAL_AUTHORITY_SENTINEL = "exchange-preflight-not-authority";
30
22
  const INTERNAL_TIMESTAMP = "1970-01-01T00:00:00.000Z";
31
- const CONTRACT_SCHEMA_VERSION = 2;
32
- const PROPOSAL_SCHEMA_VERSION = 1;
33
- const SOURCE_LOCK_SCHEMA_VERSION = 1;
34
- const PROJECTION_LOCK_SCHEMA_VERSION = 1;
35
23
  const GROUP_BY_ACTION = new Map([
36
24
  ["accept-source-change", "source-change"],
37
25
  ["register-source", "source-change"],
@@ -47,52 +35,6 @@ function uniqueSorted(values) {
47
35
  return [...new Set(values)].sort((left, right) => left.localeCompare(right));
48
36
  }
49
37
 
50
- function schemas() {
51
- return {
52
- actionPlan: ACTION_PLAN_SCHEMA_VERSION,
53
- assistBundle: ASSIST_BUNDLE_SCHEMA_VERSION,
54
- capabilities: CAPABILITIES_SCHEMA_VERSION,
55
- contract: CONTRACT_SCHEMA_VERSION,
56
- dashboardViewModel: DASHBOARD_SCHEMA_VERSION,
57
- projectionLock: PROJECTION_LOCK_SCHEMA_VERSION,
58
- projectionRenderer: RENDERER_VERSION,
59
- proposal: PROPOSAL_SCHEMA_VERSION,
60
- reviewBundle: REVIEW_BUNDLE_SCHEMA_VERSION,
61
- sourceLock: SOURCE_LOCK_SCHEMA_VERSION,
62
- };
63
- }
64
-
65
- export async function buildCapabilities(root) {
66
- const initialization = await inspectProjectInitialization(root);
67
- let project = null;
68
- if (initialization.status === "initialized") {
69
- const loaded = await loadProject(root);
70
- project = { id: loaded.contract.project.id, name: loaded.contract.project.name };
71
- }
72
- return {
73
- schemaVersion: CAPABILITIES_SCHEMA_VERSION,
74
- package: { name: "frontend-project-context", version: PACKAGE_VERSION },
75
- exchangeProtocolVersion: EXCHANGE_PROTOCOL_VERSION,
76
- schemas: schemas(),
77
- commands: [...COMMANDS],
78
- actionKinds: [...ACTION_KINDS],
79
- initialization: initialization.status,
80
- initialized: initialization.status === "initialized",
81
- project,
82
- boundaries: {
83
- provider: false,
84
- agentRuntime: false,
85
- git: false,
86
- network: false,
87
- dependencyInstallation: false,
88
- automaticApproval: false,
89
- businessCodeWrites: false,
90
- applyPlan: false,
91
- scheduler: false,
92
- daemon: false,
93
- },
94
- };
95
- }
96
38
 
97
39
  function allProjectionPaths(project) {
98
40
  return project.projectionsLock.projections.map((entry) => entry.path).sort();