@cassiomc1/forgeloop 1.6.2 → 1.6.4

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.
Files changed (127) hide show
  1. package/DOCS_INDEX.md +26 -4
  2. package/EXECUTION_STATE.md +7 -0
  3. package/LOOP_ENGINEERING.md +58 -0
  4. package/LOOP_SYSTEM_DESIGN.md +54 -0
  5. package/ORCHESTRATOR_INTEGRATION.md +23 -0
  6. package/PROTOCOL_INTEGRATION.md +36 -0
  7. package/QUALITY_SCORECARD.md +21 -0
  8. package/README.md +57 -2
  9. package/TERMINOLOGY.md +11 -0
  10. package/THREAT_MODEL.md +7 -0
  11. package/completions/_forgeloop +85 -0
  12. package/completions/forgeloop.bash +173 -0
  13. package/completions/forgeloop.fish +544 -0
  14. package/docs/AGENT_PROTOCOL_SUMMARY.md +281 -0
  15. package/docs/ARTIFACT_REFERENCE.md +226 -0
  16. package/docs/CLI_REFERENCE.md +308 -2
  17. package/docs/CODE_ATTESTATION.md +141 -0
  18. package/docs/CROSS_HARNESS_CONTINUITY.md +22 -2
  19. package/docs/DOCUMENTATION_GUIDE.md +13 -11
  20. package/docs/GETTING_STARTED.md +98 -1
  21. package/docs/MCP.md +8 -0
  22. package/docs/PLATFORM_ADAPTERS.md +69 -0
  23. package/docs/RECIPES.md +138 -0
  24. package/docs/RELEASE_CHECKLIST.md +54 -0
  25. package/docs/REVISION_PROVIDERS.md +136 -0
  26. package/docs/SIGNING_PROVIDERS.md +83 -0
  27. package/docs/TROUBLESHOOTING.md +184 -0
  28. package/docs/UNIVERSAL_INTEGRATION.md +11 -0
  29. package/docs/assets/diagrams/forgeloop-code-attestation-flow.html +13841 -0
  30. package/docs/assets/diagrams/forgeloop-code-attestation-flow.receipt.json +37 -0
  31. package/docs/assets/diagrams/forgeloop-code-attestation-flow.svg +5046 -0
  32. package/docs/assets/diagrams/forgeloop-engineering-flow.html +46 -43
  33. package/docs/assets/diagrams/forgeloop-engineering-flow.receipt.json +6 -6
  34. package/docs/assets/diagrams/forgeloop-engineering-flow.svg +41 -41
  35. package/docs/assets/diagrams/forgeloop-verification-trust-flow.html +13783 -0
  36. package/docs/assets/diagrams/forgeloop-verification-trust-flow.receipt.json +37 -0
  37. package/docs/assets/diagrams/forgeloop-verification-trust-flow.svg +4988 -0
  38. package/docs/diagrams/README.md +30 -22
  39. package/docs/diagrams/forgeloop-code-attestation-flow.workflow.json +125 -0
  40. package/docs/diagrams/forgeloop-engineering-flow.workflow.json +11 -8
  41. package/docs/diagrams/forgeloop-verification-trust-flow.workflow.json +116 -0
  42. package/docs/diagrams/manifest.json +42 -0
  43. package/docs/diagrams/reviews/forgeloop-code-attestation-flow.review.json +20 -0
  44. package/docs/diagrams/reviews/forgeloop-engineering-flow.review.json +3 -3
  45. package/docs/diagrams/reviews/forgeloop-verification-trust-flow.review.json +20 -0
  46. package/integrations/generic-ci/verify.sh +14 -0
  47. package/package.json +31 -9
  48. package/schemas/attestation-verification-result.schema.json +34 -0
  49. package/schemas/code-attestation.schema.json +61 -0
  50. package/schemas/code-manifest.schema.json +57 -0
  51. package/schemas/config.schema.json +57 -1
  52. package/schemas/handoff-envelope.schema.json +57 -0
  53. package/schemas/in-toto-statement.schema.json +32 -0
  54. package/schemas/responsibility.schema.json +37 -0
  55. package/schemas/verification-scope.schema.json +26 -0
  56. package/schemas/workspace-binding.schema.json +20 -0
  57. package/scripts/CI_VALIDATORS.md +3 -0
  58. package/scripts/benchmark-cli-startup.mjs +60 -0
  59. package/scripts/check-changelog-freshness.mjs +88 -0
  60. package/scripts/check-critical-coverage.mjs +58 -0
  61. package/scripts/generate-agent-protocol-summary.mjs +141 -0
  62. package/scripts/generate-shell-completions.mjs +132 -0
  63. package/scripts/write-forgeloop-attestation-summary.mjs +93 -0
  64. package/src/cli.js +114 -6
  65. package/src/commands/attestation-create.js +30 -0
  66. package/src/commands/attestation-status.js +12 -0
  67. package/src/commands/attestation-verify-range.js +32 -0
  68. package/src/commands/attestation-verify.js +23 -0
  69. package/src/commands/handoff-create.js +9 -0
  70. package/src/commands/handoff-list.js +12 -0
  71. package/src/commands/handoff-show.js +10 -0
  72. package/src/commands/responsibility-set.js +20 -0
  73. package/src/commands/responsibility-status.js +12 -0
  74. package/src/commands/run-check.js +43 -3
  75. package/src/commands/task-repair-legacy-recovery.js +1 -2
  76. package/src/commands/verify-scope.js +9 -0
  77. package/src/commands/workspace-bind.js +17 -0
  78. package/src/commands/workspace-status.js +19 -0
  79. package/src/core/artifact-registry.js +84 -0
  80. package/src/core/attestation-coverage.js +134 -0
  81. package/src/core/attestation-verifier.js +227 -0
  82. package/src/core/attestation.js +263 -0
  83. package/src/core/audit.js +67 -1
  84. package/src/core/bundles.js +210 -9
  85. package/src/core/cli-command-definitions.js +196 -0
  86. package/src/core/code-manifest.js +293 -0
  87. package/src/core/command-executors.js +79 -0
  88. package/src/core/command-input.js +69 -18
  89. package/src/core/command-runtime.js +2 -1
  90. package/src/core/completion.js +170 -8
  91. package/src/core/config.js +60 -2
  92. package/src/core/continuity-cli-options.js +9 -3
  93. package/src/core/continuity-reconciliation.js +20 -0
  94. package/src/core/diagnostic-projection.js +3 -1
  95. package/src/core/error-codes.js +146 -0
  96. package/src/core/events.js +109 -13
  97. package/src/core/exit-codes.js +22 -0
  98. package/src/core/handoff.js +208 -0
  99. package/src/core/information-gain-projection.js +3 -3
  100. package/src/core/integration-invocation-policy.js +40 -0
  101. package/src/core/integration-resources.js +54 -0
  102. package/src/core/next-action-phases.js +968 -0
  103. package/src/core/next-action.js +32 -951
  104. package/src/core/phase.js +29 -1
  105. package/src/core/protocol-info.js +41 -1
  106. package/src/core/repository.js +256 -11
  107. package/src/core/responsibility.js +265 -0
  108. package/src/core/revision/git.js +207 -0
  109. package/src/core/revision/provider.js +90 -0
  110. package/src/core/revision/registry.js +5 -0
  111. package/src/core/route-artifact.js +1 -1
  112. package/src/core/schema-validation.js +8 -0
  113. package/src/core/signing/none.js +23 -0
  114. package/src/core/signing/provider.js +21 -0
  115. package/src/core/signing/registry.js +7 -0
  116. package/src/core/signing/sigstore.js +158 -0
  117. package/src/core/task-command.js +9 -1
  118. package/src/core/task-paths.js +72 -0
  119. package/src/core/templates.js +8 -0
  120. package/src/core/verification-scope-capability.js +179 -0
  121. package/src/core/verification-scope.js +271 -0
  122. package/src/core/workspace-binding.js +211 -0
  123. package/src/integration.d.ts +91 -0
  124. package/AGENT_COMPATIBILITY.md +0 -11
  125. package/docs/RELEASE_CHECKLIST_1_4.md +0 -38
  126. package/docs/RELEASE_CHECKLIST_1_5_MCP.md +0 -78
  127. package/docs/RELEASE_CHECKLIST_1_6_1.md +0 -121
@@ -0,0 +1,61 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "forgeloop://schemas/code-attestation.schema.json",
4
+ "title": "ForgeLoop code attestation predicate",
5
+ "type": "object",
6
+ "required": ["schemaVersion", "protocol", "task", "content", "evidence", "verification"],
7
+ "additionalProperties": false,
8
+ "properties": {
9
+ "schemaVersion": { "const": 1 },
10
+ "protocol": {
11
+ "type": "object",
12
+ "required": ["name", "protocolVersion"],
13
+ "additionalProperties": false,
14
+ "properties": {
15
+ "name": { "const": "ForgeLoop" },
16
+ "protocolVersion": { "const": 1 }
17
+ }
18
+ },
19
+ "task": {
20
+ "type": "object",
21
+ "required": ["taskId", "verificationCycle"],
22
+ "additionalProperties": false,
23
+ "properties": {
24
+ "taskId": { "type": "string", "minLength": 1 },
25
+ "verificationCycle": { "type": "integer", "minimum": 1 }
26
+ }
27
+ },
28
+ "content": {
29
+ "type": "object",
30
+ "required": ["manifestFingerprint", "contentDigest", "coveredPaths"],
31
+ "additionalProperties": false,
32
+ "properties": {
33
+ "manifestFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
34
+ "contentDigest": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
35
+ "coveredPaths": { "type": "array", "items": { "type": "string", "minLength": 1 } }
36
+ }
37
+ },
38
+ "evidence": {
39
+ "type": "object",
40
+ "required": ["contractFingerprint", "routeFingerprint", "stateFingerprint", "receiptFingerprint", "ledgerSeq", "ledgerHash"],
41
+ "additionalProperties": false,
42
+ "properties": {
43
+ "contractFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
44
+ "routeFingerprint": { "oneOf": [{ "type": "string", "pattern": "^[a-f0-9]{64}$" }, { "type": "null" }] },
45
+ "stateFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
46
+ "receiptFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
47
+ "ledgerSeq": { "type": "integer", "minimum": 1 },
48
+ "ledgerHash": { "type": "string", "pattern": "^[a-f0-9]{64}$" }
49
+ }
50
+ },
51
+ "verification": {
52
+ "type": "object",
53
+ "required": ["completion", "audit"],
54
+ "additionalProperties": false,
55
+ "properties": {
56
+ "completion": { "const": "VALID" },
57
+ "audit": { "const": "VALID" }
58
+ }
59
+ }
60
+ }
61
+ }
@@ -0,0 +1,57 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "forgeloop://schemas/code-manifest.schema.json",
4
+ "title": "ForgeLoop code content manifest",
5
+ "type": "object",
6
+ "required": ["schemaVersion", "protocolVersion", "taskId", "verificationCycle", "capture", "bindings", "entries", "contentDigest"],
7
+ "additionalProperties": false,
8
+ "properties": {
9
+ "schemaVersion": { "const": 1 },
10
+ "protocolVersion": { "const": 1 },
11
+ "taskId": { "type": "string", "minLength": 1 },
12
+ "verificationCycle": { "type": "integer", "minimum": 1 },
13
+ "capture": {
14
+ "type": "object",
15
+ "required": ["mode", "revisionProvider", "baseRevision", "observedRevision", "providerMetadata"],
16
+ "additionalProperties": false,
17
+ "properties": {
18
+ "mode": { "enum": ["WORKTREE", "INDEX", "COMMIT"] },
19
+ "revisionProvider": { "type": "string", "minLength": 1 },
20
+ "baseRevision": { "oneOf": [{ "type": "string" }, { "type": "null" }] },
21
+ "observedRevision": { "type": "string", "minLength": 1 },
22
+ "providerMetadata": { "type": "object" }
23
+ }
24
+ },
25
+ "bindings": {
26
+ "type": "object",
27
+ "required": ["contractFingerprint", "routeFingerprint", "stateFingerprint", "receiptFingerprint", "ledgerSeq", "ledgerHash"],
28
+ "additionalProperties": false,
29
+ "properties": {
30
+ "contractFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
31
+ "routeFingerprint": { "oneOf": [{ "type": "string", "pattern": "^[a-f0-9]{64}$" }, { "type": "null" }] },
32
+ "stateFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
33
+ "receiptFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
34
+ "ledgerSeq": { "type": "integer", "minimum": 1 },
35
+ "ledgerHash": { "type": "string", "pattern": "^[a-f0-9]{64}$" }
36
+ }
37
+ },
38
+ "entries": {
39
+ "type": "array",
40
+ "items": {
41
+ "type": "object",
42
+ "required": ["path", "operation", "kind"],
43
+ "additionalProperties": false,
44
+ "properties": {
45
+ "path": { "type": "string", "minLength": 1 },
46
+ "sourcePath": { "type": "string", "minLength": 1 },
47
+ "operation": { "enum": ["ADDED", "MODIFIED", "DELETED", "RENAMED", "COPIED", "TYPE_CHANGED"] },
48
+ "kind": { "enum": ["FILE", "SYMLINK", "GITLINK", "DELETED"] },
49
+ "sha256": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
50
+ "providerContentId": { "type": "string", "minLength": 1 },
51
+ "providerMetadata": { "type": "object" }
52
+ }
53
+ }
54
+ },
55
+ "contentDigest": { "type": "string", "pattern": "^[a-f0-9]{64}$" }
56
+ }
57
+ }
@@ -10,7 +10,63 @@
10
10
  "complianceMode": { "enum": ["advisory", "standard", "strict"] },
11
11
  "policy": { "type": "string", "minLength": 1 },
12
12
  "requiredGates": { "type": "array", "items": { "type": "string", "minLength": 1 } },
13
- "requiredEvidence": { "type": "array", "items": { "type": "string", "minLength": 1 } }
13
+ "requiredEvidence": { "type": "array", "items": { "type": "string", "minLength": 1 } },
14
+ "verification": {
15
+ "type": "object",
16
+ "required": ["checkers"],
17
+ "additionalProperties": false,
18
+ "properties": {
19
+ "checkers": {
20
+ "type": "array",
21
+ "items": {
22
+ "type": "object",
23
+ "required": ["checkId", "scopeMode", "argvPrefix", "pathInsertion"],
24
+ "additionalProperties": false,
25
+ "properties": {
26
+ "checkId": { "type": "string", "minLength": 1 },
27
+ "scopeMode": { "const": "PATH_ARGUMENTS" },
28
+ "argvPrefix": { "type": "array", "minItems": 1, "items": { "type": "string", "minLength": 1 } },
29
+ "pathInsertion": { "const": "APPEND" }
30
+ }
31
+ }
32
+ }
33
+ }
34
+ },
35
+ "attestation": {
36
+ "type": "object",
37
+ "required": ["mode", "revisionProvider", "requireCompleteCoverage", "coverage", "signing"],
38
+ "additionalProperties": false,
39
+ "properties": {
40
+ "mode": { "enum": ["off", "optional", "required"] },
41
+ "revisionProvider": { "type": "string", "minLength": 1 },
42
+ "requireCompleteCoverage": { "type": "boolean" },
43
+ "coverage": {
44
+ "type": "object",
45
+ "required": ["exclude"],
46
+ "additionalProperties": false,
47
+ "properties": { "exclude": { "type": "array", "items": { "type": "string", "minLength": 1 } } }
48
+ },
49
+ "signing": {
50
+ "type": "object",
51
+ "required": ["provider", "required", "policy"],
52
+ "additionalProperties": false,
53
+ "properties": {
54
+ "provider": { "enum": ["none", "sigstore"] },
55
+ "required": { "type": "boolean" },
56
+ "policy": {
57
+ "type": "object",
58
+ "required": ["identities", "requireTransparencyLog"],
59
+ "additionalProperties": false,
60
+ "properties": {
61
+ "issuer": { "type": "string" },
62
+ "identities": { "type": "array", "items": { "type": "string", "minLength": 1 } },
63
+ "requireTransparencyLog": { "type": "boolean" }
64
+ }
65
+ }
66
+ }
67
+ }
68
+ }
69
+ }
14
70
  },
15
71
  "additionalProperties": false
16
72
  }
@@ -0,0 +1,57 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "forgeloop://schemas/handoff-envelope.schema.json",
4
+ "title": "ForgeLoop canonical handoff envelope",
5
+ "type": "object",
6
+ "required": ["schemaVersion", "protocolVersion", "handoffId", "taskId", "createdAt", "intent", "state", "evidence", "continuity", "artifactDigest"],
7
+ "additionalProperties": false,
8
+ "properties": {
9
+ "schemaVersion": { "const": 1 },
10
+ "protocolVersion": { "const": 1 },
11
+ "handoffId": { "type": "string", "pattern": "^handoff-[A-Za-z0-9_-]+$" },
12
+ "taskId": { "type": "string", "minLength": 1 },
13
+ "createdAt": { "type": "string", "minLength": 1 },
14
+ "intent": {
15
+ "type": "object",
16
+ "additionalProperties": false,
17
+ "properties": {
18
+ "recipientHint": { "type": "string", "minLength": 1 },
19
+ "note": { "type": "string", "minLength": 1 }
20
+ }
21
+ },
22
+ "state": {
23
+ "type": "object",
24
+ "required": ["phase", "revision", "verificationCycle", "contractFingerprint", "routeFingerprint", "repositoryFingerprint", "writeClaims", "changedPaths"],
25
+ "additionalProperties": false,
26
+ "properties": {
27
+ "phase": { "type": "string", "minLength": 1 },
28
+ "revision": { "type": "integer", "minimum": 0 },
29
+ "verificationCycle": { "type": "integer", "minimum": 1 },
30
+ "contractFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
31
+ "routeFingerprint": { "oneOf": [{ "type": "string", "pattern": "^[a-f0-9]{64}$" }, { "type": "null" }] },
32
+ "repositoryFingerprint": { "type": "object" },
33
+ "writeClaims": { "type": "array", "items": { "type": "string" } },
34
+ "changedPaths": { "type": "array", "items": { "type": "string" } }
35
+ }
36
+ },
37
+ "evidence": {
38
+ "type": "object",
39
+ "required": ["executionRefs", "checkIds"],
40
+ "additionalProperties": false,
41
+ "properties": {
42
+ "executionRefs": { "type": "array", "items": { "type": "string" } },
43
+ "checkIds": { "type": "array", "items": { "type": "string" } }
44
+ }
45
+ },
46
+ "continuity": {
47
+ "type": "object",
48
+ "required": ["ref", "fingerprint"],
49
+ "additionalProperties": false,
50
+ "properties": {
51
+ "ref": { "type": "string", "minLength": 1 },
52
+ "fingerprint": { "oneOf": [{ "type": "string", "pattern": "^[a-f0-9]{64}$" }, { "type": "null" }] }
53
+ }
54
+ },
55
+ "artifactDigest": { "type": "string", "pattern": "^[a-f0-9]{64}$" }
56
+ }
57
+ }
@@ -0,0 +1,32 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "forgeloop://schemas/in-toto-statement.schema.json",
4
+ "title": "ForgeLoop in-toto Statement v1",
5
+ "type": "object",
6
+ "required": ["schemaVersion", "_type", "subject", "predicateType", "predicate"],
7
+ "additionalProperties": false,
8
+ "properties": {
9
+ "schemaVersion": { "const": 1 },
10
+ "_type": { "const": "https://in-toto.io/Statement/v1" },
11
+ "subject": {
12
+ "type": "array",
13
+ "minItems": 1,
14
+ "items": {
15
+ "type": "object",
16
+ "required": ["name", "digest"],
17
+ "additionalProperties": false,
18
+ "properties": {
19
+ "name": { "type": "string", "minLength": 1 },
20
+ "digest": {
21
+ "type": "object",
22
+ "required": ["sha256"],
23
+ "additionalProperties": false,
24
+ "properties": { "sha256": { "type": "string", "pattern": "^[a-f0-9]{64}$" } }
25
+ }
26
+ }
27
+ }
28
+ },
29
+ "predicateType": { "const": "https://forgeloop.dev/attestation/v1" },
30
+ "predicate": { "type": "object" }
31
+ }
32
+ }
@@ -0,0 +1,37 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "forgeloop://schemas/responsibility.schema.json",
4
+ "title": "ForgeLoop responsibility constraints",
5
+ "type": "object",
6
+ "required": ["schemaVersion", "protocolVersion", "taskId", "label", "createdAt", "allowedPaths", "readOnlyPaths", "requiredCheckIds", "frozenInputs", "baseline"],
7
+ "additionalProperties": false,
8
+ "properties": {
9
+ "schemaVersion": { "const": 1 },
10
+ "protocolVersion": { "const": 1 },
11
+ "taskId": { "type": "string", "minLength": 1 },
12
+ "label": { "type": "string", "minLength": 1 },
13
+ "createdAt": { "type": "string", "minLength": 1 },
14
+ "allowedPaths": { "type": "array", "items": { "type": "string", "minLength": 1 } },
15
+ "readOnlyPaths": { "type": "array", "items": { "type": "string", "minLength": 1 } },
16
+ "requiredCheckIds": { "type": "array", "items": { "type": "string", "minLength": 1 } },
17
+ "frozenInputs": {
18
+ "type": "object",
19
+ "additionalProperties": false,
20
+ "properties": {
21
+ "contract": { "type": "boolean" },
22
+ "route": { "type": "boolean" },
23
+ "claims": { "type": "boolean" }
24
+ }
25
+ },
26
+ "baseline": {
27
+ "type": "object",
28
+ "required": ["contractFingerprint", "routeFingerprint", "claimsFingerprint"],
29
+ "additionalProperties": false,
30
+ "properties": {
31
+ "contractFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
32
+ "routeFingerprint": { "oneOf": [{ "type": "string", "pattern": "^[a-f0-9]{64}$" }, { "type": "null" }] },
33
+ "claimsFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" }
34
+ }
35
+ }
36
+ }
37
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "forgeloop://schemas/verification-scope.schema.json",
4
+ "title": "ForgeLoop verification scope",
5
+ "type": "object",
6
+ "required": ["schemaVersion", "protocolVersion", "taskId", "requestedMode", "resolvedMode", "verificationCycle", "changedPaths", "claimedPaths", "selectedPaths", "reasons", "fallback", "contractFingerprint", "repositoryFingerprint", "claimsFingerprint", "createdAt"],
7
+ "additionalProperties": false,
8
+ "properties": {
9
+ "schemaVersion": { "const": 1 },
10
+ "protocolVersion": { "const": 1 },
11
+ "taskId": { "type": "string", "minLength": 1 },
12
+ "requestedMode": { "enum": ["AUTO", "CHANGED", "CLAIMED", "FULL"] },
13
+ "resolvedMode": { "enum": ["CHANGED", "CLAIMED", "FULL", "UNRESOLVED"] },
14
+ "verificationCycle": { "type": "integer", "minimum": 1 },
15
+ "changedPaths": { "type": "array", "items": { "type": "string" } },
16
+ "claimedPaths": { "type": "array", "items": { "type": "string" } },
17
+ "selectedPaths": { "type": "array", "items": { "type": "string" } },
18
+ "reasons": { "type": "array", "items": { "type": "string", "minLength": 1 } },
19
+ "fallback": { "oneOf": [{ "type": "null" }, { "type": "object" }] },
20
+ "contractFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
21
+ "repositoryFingerprint": { "type": "object" },
22
+ "claimsFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
23
+ "checkerCapabilityFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
24
+ "createdAt": { "type": "string", "minLength": 1 }
25
+ }
26
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "forgeloop://schemas/workspace-binding.schema.json",
4
+ "title": "ForgeLoop task workspace binding",
5
+ "type": "object",
6
+ "required": ["schemaVersion", "protocolVersion", "taskId", "mode", "repositoryIdentity", "workspaceIdentity", "branchAtBind", "headAtBind", "boundAt"],
7
+ "additionalProperties": false,
8
+ "properties": {
9
+ "schemaVersion": { "const": 1 },
10
+ "protocolVersion": { "const": 1 },
11
+ "taskId": { "type": "string", "minLength": 1 },
12
+ "mode": { "const": "GIT_WORKTREE" },
13
+ "repositoryIdentity": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
14
+ "workspaceIdentity": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
15
+ "branchAtBind": { "oneOf": [{ "type": "string" }, { "type": "null" }] },
16
+ "headAtBind": { "oneOf": [{ "type": "string" }, { "type": "null" }] },
17
+ "boundAt": { "type": "string", "minLength": 1 },
18
+ "metadata": { "type": "object" }
19
+ }
20
+ }
@@ -16,6 +16,9 @@ change the historical scanner and validator contracts without a dedicated
16
16
  parity project. They are not runtime dependencies, are not shipped as the CLI
17
17
  runtime, and are not required by `npm install`.
18
18
 
19
+ The minimum supported Python version for these validators is Python 3.9. CI
20
+ must select or verify Python 3.9 or newer before invoking the frozen commands.
21
+
19
22
  ## CI invocation
20
23
 
21
24
  ```bash
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawnSync } from "node:child_process";
4
+ import { performance } from "node:perf_hooks";
5
+ import path from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+
8
+ const repositoryRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
9
+ const DEFAULT_ITERATIONS = 7;
10
+ const DEFAULT_BUDGET_MS = 1000;
11
+
12
+ function numericArgument(name, fallback) {
13
+ const index = process.argv.indexOf(name);
14
+ if (index === -1) return fallback;
15
+ const value = Number(process.argv[index + 1]);
16
+ if (!Number.isFinite(value) || value <= 0) throw new Error(`${name} requires a positive number`);
17
+ return value;
18
+ }
19
+
20
+ export function benchmarkCliStartup({
21
+ iterations = DEFAULT_ITERATIONS,
22
+ budgetMs = DEFAULT_BUDGET_MS,
23
+ spawnProcess = spawnSync,
24
+ cwd = repositoryRoot,
25
+ } = {}) {
26
+ const samples = [];
27
+ for (let index = 0; index < iterations; index += 1) {
28
+ const started = performance.now();
29
+ const result = spawnProcess(process.execPath, ["src/cli.js", "protocol-info", "--json"], {
30
+ cwd,
31
+ stdio: "ignore",
32
+ shell: false,
33
+ });
34
+ const elapsed = performance.now() - started;
35
+ if (result.status !== 0) {
36
+ throw new Error(`protocol-info startup command failed with exit code ${result.status ?? "unknown"}`);
37
+ }
38
+ samples.push(elapsed);
39
+ }
40
+ const sorted = [...samples].sort((left, right) => left - right);
41
+ const median = sorted[Math.floor(sorted.length / 2)];
42
+ return { iterations, budgetMs, medianMs: median, samplesMs: samples };
43
+ }
44
+
45
+ if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
46
+ try {
47
+ const result = benchmarkCliStartup({
48
+ iterations: numericArgument("--iterations", DEFAULT_ITERATIONS),
49
+ budgetMs: numericArgument("--budget-ms", DEFAULT_BUDGET_MS),
50
+ });
51
+ console.log(`CLI startup median: ${result.medianMs.toFixed(1)} ms (budget: ${result.budgetMs} ms, samples: ${result.iterations})`);
52
+ if (result.medianMs > result.budgetMs) {
53
+ console.error("CLI startup budget exceeded.");
54
+ process.exitCode = 1;
55
+ }
56
+ } catch (error) {
57
+ console.error(`CLI startup benchmark failed: ${error.message}`);
58
+ process.exitCode = 1;
59
+ }
60
+ }
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { execFileSync } from "node:child_process";
4
+ import { readFile } from "node:fs/promises";
5
+ import path from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+
8
+ const repositoryRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
9
+
10
+ function git(args, cwd = repositoryRoot) {
11
+ return execFileSync("git", args, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
12
+ }
13
+
14
+ export function latestReleaseTag(tags) {
15
+ return tags
16
+ .map((tag) => tag.trim())
17
+ .filter((tag) => /^v?\d+\.\d+\.\d+$/u.test(tag))
18
+ .sort((left, right) => left.localeCompare(right, undefined, { numeric: true })).at(-1) ?? null;
19
+ }
20
+
21
+ export function unreleasedSection(changelog) {
22
+ const heading = /^## Unreleased\s*$/imu.exec(changelog);
23
+ if (!heading) return "";
24
+ const rest = changelog.slice(heading.index + heading[0].length);
25
+ const nextHeading = /^##\s/imu.exec(rest);
26
+ return nextHeading ? rest.slice(0, nextHeading.index) : rest;
27
+ }
28
+
29
+ export function stripHtmlComments(value) {
30
+ const chunks = [];
31
+ let cursor = 0;
32
+
33
+ while (cursor < value.length) {
34
+ const commentStart = value.indexOf("<!--", cursor);
35
+ if (commentStart === -1) {
36
+ chunks.push(value.slice(cursor));
37
+ break;
38
+ }
39
+
40
+ chunks.push(value.slice(cursor, commentStart));
41
+ const commentEnd = value.indexOf("-->", commentStart + 4);
42
+ if (commentEnd === -1) break;
43
+ cursor = commentEnd + 3;
44
+ }
45
+
46
+ return chunks.join("");
47
+ }
48
+
49
+ export function hasMeaningfulUnreleasedContent(section) {
50
+ return stripHtmlComments(section)
51
+ .split("\n")
52
+ .map((line) => line.trim())
53
+ .some((line) => line && !/^No changes\.?$/iu.test(line));
54
+ }
55
+
56
+ export function checkChangelogFreshness({ changelog, tags = [], commitsSinceLatestTag = 0, allowEmpty = false } = {}) {
57
+ const latestTag = latestReleaseTag(tags);
58
+ const section = unreleasedSection(changelog);
59
+ const hasChanges = commitsSinceLatestTag > 0;
60
+ const populated = hasMeaningfulUnreleasedContent(section);
61
+ const ok = allowEmpty || !hasChanges || populated;
62
+ return { ok, latestTag, hasChanges, populated, section };
63
+ }
64
+
65
+ async function run() {
66
+ const changelog = await readFile(path.join(repositoryRoot, "CHANGELOG.md"), "utf8");
67
+ const tags = git(["tag", "--list"]).split("\n").filter(Boolean);
68
+ const latestTag = latestReleaseTag(tags);
69
+ const commitsSinceLatestTag = latestTag
70
+ ? Number(git(["rev-list", "--count", `${latestTag}..HEAD`]))
71
+ : Number(git(["rev-list", "--count", "HEAD"]));
72
+ const result = checkChangelogFreshness({ changelog, tags, commitsSinceLatestTag, allowEmpty: process.argv.includes("--allow-empty") });
73
+ if (!result.ok) {
74
+ console.error(`CHANGELOG.md has changes since ${result.latestTag ?? "the initial commit"} but the Unreleased section is empty.`);
75
+ process.exitCode = 1;
76
+ return;
77
+ }
78
+ console.log(`Changelog freshness valid: ${result.latestTag ?? "no release tag"}`);
79
+ }
80
+
81
+ if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
82
+ try {
83
+ await run();
84
+ } catch (error) {
85
+ console.error(`Changelog freshness check failed: ${error.message}`);
86
+ process.exitCode = 1;
87
+ }
88
+ }
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readFile } from "node:fs/promises";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ const repositoryRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
8
+ const summaryPath = path.join(repositoryRoot, "coverage", "coverage-summary.json");
9
+
10
+ export const CRITICAL_COVERAGE_THRESHOLDS = Object.freeze({
11
+ "src/core/repository.js": { lines: 80, branches: 60 },
12
+ "src/core/policy-discovery.js": { lines: 75, branches: 45 },
13
+ "src/core/policy-adapters.js": { lines: 70, branches: 45 },
14
+ "src/core/task-identity.js": { lines: 75, branches: 50 },
15
+ "src/core/task-migration-validation.js": { lines: 75, branches: 50 },
16
+ });
17
+
18
+ function findSummaryEntry(summary, relativePath) {
19
+ const absolutePath = path.join(repositoryRoot, relativePath);
20
+ return summary[absolutePath] ?? summary[relativePath] ?? summary[path.resolve(relativePath)];
21
+ }
22
+
23
+ export async function checkCriticalCoverage({ summary = null, readSummary = readFile } = {}) {
24
+ const value = summary ?? JSON.parse(await readSummary(summaryPath, "utf8"));
25
+ const failures = [];
26
+ const missing = [];
27
+ for (const [file, thresholds] of Object.entries(CRITICAL_COVERAGE_THRESHOLDS)) {
28
+ const entry = findSummaryEntry(value, file);
29
+ if (!entry) {
30
+ missing.push(file);
31
+ continue;
32
+ }
33
+ for (const metric of ["lines", "branches"]) {
34
+ const actual = entry[metric]?.pct;
35
+ if (typeof actual !== "number" || actual < thresholds[metric]) {
36
+ failures.push({ file, metric, actual, required: thresholds[metric] });
37
+ }
38
+ }
39
+ }
40
+ return { ok: failures.length === 0 && missing.length === 0, failures, missing };
41
+ }
42
+
43
+ if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
44
+ try {
45
+ const result = await checkCriticalCoverage();
46
+ if (result.missing.length > 0) {
47
+ console.error(`Critical coverage report is missing: ${result.missing.join(", ")}`);
48
+ }
49
+ for (const failure of result.failures) {
50
+ console.error(`${failure.file} ${failure.metric}: ${failure.actual ?? "unknown"}% (required ${failure.required}%)`);
51
+ }
52
+ if (!result.ok) process.exitCode = 1;
53
+ else console.log("Critical-module coverage meets the configured thresholds.");
54
+ } catch (error) {
55
+ console.error(`Critical coverage check failed: ${error.message}`);
56
+ process.exitCode = 1;
57
+ }
58
+ }