@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.
- package/DOCS_INDEX.md +26 -4
- package/EXECUTION_STATE.md +7 -0
- package/LOOP_ENGINEERING.md +58 -0
- package/LOOP_SYSTEM_DESIGN.md +54 -0
- package/ORCHESTRATOR_INTEGRATION.md +23 -0
- package/PROTOCOL_INTEGRATION.md +36 -0
- package/QUALITY_SCORECARD.md +21 -0
- package/README.md +57 -2
- package/TERMINOLOGY.md +11 -0
- package/THREAT_MODEL.md +7 -0
- package/completions/_forgeloop +85 -0
- package/completions/forgeloop.bash +173 -0
- package/completions/forgeloop.fish +544 -0
- package/docs/AGENT_PROTOCOL_SUMMARY.md +281 -0
- package/docs/ARTIFACT_REFERENCE.md +226 -0
- package/docs/CLI_REFERENCE.md +308 -2
- package/docs/CODE_ATTESTATION.md +141 -0
- package/docs/CROSS_HARNESS_CONTINUITY.md +22 -2
- package/docs/DOCUMENTATION_GUIDE.md +13 -11
- package/docs/GETTING_STARTED.md +98 -1
- package/docs/MCP.md +8 -0
- package/docs/PLATFORM_ADAPTERS.md +69 -0
- package/docs/RECIPES.md +138 -0
- package/docs/RELEASE_CHECKLIST.md +54 -0
- package/docs/REVISION_PROVIDERS.md +136 -0
- package/docs/SIGNING_PROVIDERS.md +83 -0
- package/docs/TROUBLESHOOTING.md +184 -0
- package/docs/UNIVERSAL_INTEGRATION.md +11 -0
- package/docs/assets/diagrams/forgeloop-code-attestation-flow.html +13841 -0
- package/docs/assets/diagrams/forgeloop-code-attestation-flow.receipt.json +37 -0
- package/docs/assets/diagrams/forgeloop-code-attestation-flow.svg +5046 -0
- package/docs/assets/diagrams/forgeloop-engineering-flow.html +46 -43
- package/docs/assets/diagrams/forgeloop-engineering-flow.receipt.json +6 -6
- package/docs/assets/diagrams/forgeloop-engineering-flow.svg +41 -41
- package/docs/assets/diagrams/forgeloop-verification-trust-flow.html +13783 -0
- package/docs/assets/diagrams/forgeloop-verification-trust-flow.receipt.json +37 -0
- package/docs/assets/diagrams/forgeloop-verification-trust-flow.svg +4988 -0
- package/docs/diagrams/README.md +30 -22
- package/docs/diagrams/forgeloop-code-attestation-flow.workflow.json +125 -0
- package/docs/diagrams/forgeloop-engineering-flow.workflow.json +11 -8
- package/docs/diagrams/forgeloop-verification-trust-flow.workflow.json +116 -0
- package/docs/diagrams/manifest.json +42 -0
- package/docs/diagrams/reviews/forgeloop-code-attestation-flow.review.json +20 -0
- package/docs/diagrams/reviews/forgeloop-engineering-flow.review.json +3 -3
- package/docs/diagrams/reviews/forgeloop-verification-trust-flow.review.json +20 -0
- package/integrations/generic-ci/verify.sh +14 -0
- package/package.json +31 -9
- package/schemas/attestation-verification-result.schema.json +34 -0
- package/schemas/code-attestation.schema.json +61 -0
- package/schemas/code-manifest.schema.json +57 -0
- package/schemas/config.schema.json +57 -1
- package/schemas/handoff-envelope.schema.json +57 -0
- package/schemas/in-toto-statement.schema.json +32 -0
- package/schemas/responsibility.schema.json +37 -0
- package/schemas/verification-scope.schema.json +26 -0
- package/schemas/workspace-binding.schema.json +20 -0
- package/scripts/CI_VALIDATORS.md +3 -0
- package/scripts/benchmark-cli-startup.mjs +60 -0
- package/scripts/check-changelog-freshness.mjs +88 -0
- package/scripts/check-critical-coverage.mjs +58 -0
- package/scripts/generate-agent-protocol-summary.mjs +141 -0
- package/scripts/generate-shell-completions.mjs +132 -0
- package/scripts/write-forgeloop-attestation-summary.mjs +93 -0
- package/src/cli.js +114 -6
- package/src/commands/attestation-create.js +30 -0
- package/src/commands/attestation-status.js +12 -0
- package/src/commands/attestation-verify-range.js +32 -0
- package/src/commands/attestation-verify.js +23 -0
- package/src/commands/handoff-create.js +9 -0
- package/src/commands/handoff-list.js +12 -0
- package/src/commands/handoff-show.js +10 -0
- package/src/commands/responsibility-set.js +20 -0
- package/src/commands/responsibility-status.js +12 -0
- package/src/commands/run-check.js +43 -3
- package/src/commands/task-repair-legacy-recovery.js +1 -2
- package/src/commands/verify-scope.js +9 -0
- package/src/commands/workspace-bind.js +17 -0
- package/src/commands/workspace-status.js +19 -0
- package/src/core/artifact-registry.js +84 -0
- package/src/core/attestation-coverage.js +134 -0
- package/src/core/attestation-verifier.js +227 -0
- package/src/core/attestation.js +263 -0
- package/src/core/audit.js +67 -1
- package/src/core/bundles.js +210 -9
- package/src/core/cli-command-definitions.js +196 -0
- package/src/core/code-manifest.js +293 -0
- package/src/core/command-executors.js +79 -0
- package/src/core/command-input.js +69 -18
- package/src/core/command-runtime.js +2 -1
- package/src/core/completion.js +170 -8
- package/src/core/config.js +60 -2
- package/src/core/continuity-cli-options.js +9 -3
- package/src/core/continuity-reconciliation.js +20 -0
- package/src/core/diagnostic-projection.js +3 -1
- package/src/core/error-codes.js +146 -0
- package/src/core/events.js +109 -13
- package/src/core/exit-codes.js +22 -0
- package/src/core/handoff.js +208 -0
- package/src/core/information-gain-projection.js +3 -3
- package/src/core/integration-invocation-policy.js +40 -0
- package/src/core/integration-resources.js +54 -0
- package/src/core/next-action-phases.js +968 -0
- package/src/core/next-action.js +32 -951
- package/src/core/phase.js +29 -1
- package/src/core/protocol-info.js +41 -1
- package/src/core/repository.js +256 -11
- package/src/core/responsibility.js +265 -0
- package/src/core/revision/git.js +207 -0
- package/src/core/revision/provider.js +90 -0
- package/src/core/revision/registry.js +5 -0
- package/src/core/route-artifact.js +1 -1
- package/src/core/schema-validation.js +8 -0
- package/src/core/signing/none.js +23 -0
- package/src/core/signing/provider.js +21 -0
- package/src/core/signing/registry.js +7 -0
- package/src/core/signing/sigstore.js +158 -0
- package/src/core/task-command.js +9 -1
- package/src/core/task-paths.js +72 -0
- package/src/core/templates.js +8 -0
- package/src/core/verification-scope-capability.js +179 -0
- package/src/core/verification-scope.js +271 -0
- package/src/core/workspace-binding.js +211 -0
- package/src/integration.d.ts +91 -0
- package/AGENT_COMPATIBILITY.md +0 -11
- package/docs/RELEASE_CHECKLIST_1_4.md +0 -38
- package/docs/RELEASE_CHECKLIST_1_5_MCP.md +0 -78
- 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
|
+
}
|
package/scripts/CI_VALIDATORS.md
CHANGED
|
@@ -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
|
+
}
|