@cassiomc1/forgeloop 1.8.1 → 1.10.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.
- package/.cursor/rules/project-loop.mdc +6 -3
- package/.github/copilot-instructions.md +5 -0
- package/AGENTS.md +6 -0
- package/AGENT_COMPATIBILITY.md +15 -0
- package/CLAUDE.md +6 -0
- package/DELEGATION_PROTOCOL.md +6 -0
- package/DOCS_INDEX.md +9 -2
- package/ENG/accessibility-eng.md +12 -2
- package/ENG/design-code-eng.md +22 -1
- package/LOOP_ENGINEERING.md +41 -0
- package/LOOP_SYSTEM_DESIGN.md +33 -0
- package/ORCHESTRATOR_INTEGRATION.md +9 -0
- package/PROTOCOL_INTEGRATION.md +57 -0
- package/QUALITY_SCORECARD.md +2 -0
- package/README.md +51 -0
- package/TERMINOLOGY.md +12 -0
- package/THREAT_MODEL.md +48 -0
- package/completions/_forgeloop +5 -1
- package/completions/forgeloop.bash +9 -1
- package/completions/forgeloop.fish +27 -1
- package/docs/ADVISORY_CONTEXT.md +174 -0
- package/docs/AGENT_PROTOCOL_SUMMARY.md +33 -2
- package/docs/ARTIFACT_REFERENCE.md +142 -0
- package/docs/CLI_REFERENCE.md +124 -1
- package/docs/CROSS_HARNESS_CONTINUITY.md +85 -0
- package/docs/DOCUMENTATION_GUIDE.md +7 -0
- package/docs/GETTING_STARTED.md +22 -0
- package/docs/KNOWLEDGE_SOURCES.md +171 -0
- package/docs/MCP.md +17 -1
- package/docs/RECIPES.md +111 -0
- package/docs/RELEASE_CHECKLIST.md +14 -0
- package/docs/STRUCTURAL_QUALITY.md +350 -0
- package/docs/TROUBLESHOOTING.md +161 -2
- package/docs/UNIVERSAL_INTEGRATION.md +60 -0
- package/package.json +4 -1
- package/schemas/config.schema.json +46 -0
- package/schemas/handoff-envelope.schema.json +1 -0
- package/schemas/preflight.schema.json +2 -1
- package/schemas/structural-quality.schema.json +175 -0
- package/scripts/check-changelog-freshness.mjs +27 -3
- package/scripts/generate-agent-protocol-summary.mjs +18 -0
- package/src/cli.js +24 -0
- package/src/commands/handoff-accept.js +36 -0
- package/src/commands/handoff-list.js +28 -2
- package/src/commands/handoff-show.js +27 -2
- package/src/commands/quality-baseline.js +28 -0
- package/src/commands/quality-status.js +34 -0
- package/src/commands/quality-verify.js +30 -0
- package/src/commands/reconcile-continuity.js +4 -0
- package/src/core/advisory-context/constants.js +74 -0
- package/src/core/advisory-context/provider.js +287 -0
- package/src/core/advisory-context/service.js +140 -0
- package/src/core/artifact-registry.js +12 -0
- package/src/core/audit.js +38 -0
- package/src/core/bundles.js +134 -1
- package/src/core/cli-command-definitions.js +62 -0
- package/src/core/command-executors.js +28 -0
- package/src/core/command-input.js +23 -1
- package/src/core/completion-artifacts.js +2 -0
- package/src/core/completion.js +42 -0
- package/src/core/config.js +3 -0
- package/src/core/continuity-lint.js +89 -0
- package/src/core/continuity-reconciliation.js +16 -0
- package/src/core/continuity.js +10 -11
- package/src/core/error-codes.js +186 -0
- package/src/core/events.js +32 -0
- package/src/core/execution-profile-context.js +15 -1
- package/src/core/filesystem.js +34 -3
- package/src/core/handoff-acceptance.js +277 -0
- package/src/core/handoff.js +41 -8
- package/src/core/inspect.js +64 -0
- package/src/core/integration-invocation-policy.js +34 -2
- package/src/core/integration-resources.js +38 -1
- package/src/core/next-action-model.js +11 -1
- package/src/core/next-action-phases.js +84 -5
- package/src/core/phase.js +9 -1
- package/src/core/portable-context.js +103 -0
- package/src/core/preflight.js +33 -0
- package/src/core/protocol-info.js +33 -2
- package/src/core/runtime-context.js +58 -0
- package/src/core/schema-validation.js +1 -0
- package/src/core/structural-quality/artifacts.js +329 -0
- package/src/core/structural-quality/constants.js +67 -0
- package/src/core/structural-quality/policy.js +227 -0
- package/src/core/structural-quality/provider.js +287 -0
- package/src/core/structural-quality/sentrux-mcp.js +477 -0
- package/src/core/structural-quality/service.js +1138 -0
- package/src/core/structural-quality/source-fingerprint.js +112 -0
- package/src/core/structural-quality/status.js +3 -0
- package/src/core/task-paths.js +24 -0
- package/src/core/templates.js +1 -0
- package/src/integration.d.ts +141 -0
- package/src/integration.js +36 -0
package/docs/TROUBLESHOOTING.md
CHANGED
|
@@ -29,6 +29,10 @@ This guide provides symptom-first recovery procedures for common ForgeLoop proto
|
|
|
29
29
|
- [Responsibility contract rejects a pass](#symptom-responsibility-contract-rejects-a-pass)
|
|
30
30
|
- [Verification scope is stale or unresolved](#symptom-verification-scope-is-stale-or-unresolved)
|
|
31
31
|
- [Attestation or revision coverage is invalid](#symptom-attestation-or-revision-coverage-is-invalid)
|
|
32
|
+
- [Structural-quality evidence is missing or blocked](#symptom-structural-quality-evidence-is-missing-or-blocked)
|
|
33
|
+
- [Structural-quality verification reports a regression](#symptom-structural-quality-verification-reports-a-regression)
|
|
34
|
+
- [Structural-quality provider is unavailable or invalid](#symptom-structural-quality-provider-is-unavailable-or-invalid)
|
|
35
|
+
- [Structural-quality evidence is stale or incomparable](#symptom-structural-quality-evidence-is-stale-or-incomparable)
|
|
32
36
|
- [Another harness cannot resume the task](#symptom-another-harness-cannot-resume)
|
|
33
37
|
- [Task claim conflict or recovered task](#symptom-task-creation-blocked-by-a-write-claim-conflict-e_task_scope_conflict)
|
|
34
38
|
- [Stable Error & Reason Code Reference](#stable-error-and-reason-codes)
|
|
@@ -757,16 +761,50 @@ explicit operation only when the task's scope permits it.
|
|
|
757
761
|
|
|
758
762
|
---
|
|
759
763
|
|
|
764
|
+
### Symptom: Advisory Context Recall Is Unavailable or Rejected
|
|
765
|
+
|
|
766
|
+
#### Error Codes: `E_ADVISORY_CONTEXT_PROVIDER_INVALID`, `E_ADVISORY_CONTEXT_PROVIDER_UNAVAILABLE`, `E_ADVISORY_CONTEXT_QUERY_INVALID`, `E_ADVISORY_CONTEXT_REQUEST_INVALID`, `E_ADVISORY_CONTEXT_RESULT_INVALID`, `E_ADVISORY_CONTEXT_TIMEOUT`, `E_ADVISORY_CONTEXT_OUTPUT_LIMIT`, `E_PORTABLE_CONTEXT_INVALID`
|
|
767
|
+
|
|
768
|
+
#### What it means
|
|
769
|
+
|
|
770
|
+
The host's optional advisory provider is missing, malformed, unavailable, too
|
|
771
|
+
slow, or returned content outside the bounded portable-context contract. These
|
|
772
|
+
failures never become lifecycle, evidence, completion, or next-action failures.
|
|
773
|
+
|
|
774
|
+
#### Safe recovery
|
|
775
|
+
|
|
776
|
+
Inspect provider registration and normalize the request before retrying through
|
|
777
|
+
the Integration API. Reduce query, item, total-output, and timeout values to the
|
|
778
|
+
documented budgets, remove secrets/control characters, and ensure the provider
|
|
779
|
+
returns only allowlisted item fields. If advisory context is unavailable,
|
|
780
|
+
continue from canonical task state; do not fabricate a provider or execute its
|
|
781
|
+
text.
|
|
782
|
+
|
|
783
|
+
### Symptom: Continuity Lint Reports a Contradictory Hint
|
|
784
|
+
|
|
785
|
+
Continuity lint is diagnostic only. Its findings do not change reconciliation,
|
|
786
|
+
state, evidence, authority, or completion:
|
|
787
|
+
|
|
788
|
+
| Finding | Meaning | Safe response |
|
|
789
|
+
| --- | --- | --- |
|
|
790
|
+
| `CONTINUITY_REMAINING_ALREADY_COMPLETED` | A remaining-work item is already recorded as completed. | Refresh the operational note; do not change canonical completion evidence. |
|
|
791
|
+
| `CONTINUITY_FOCUS_ALREADY_COMPLETED` | The current focus ID is already completed. | Choose a current inspection focus or clear the hint. |
|
|
792
|
+
| `CONTINUITY_ITEM_ROLE_CONFLICT` | An item appears in both remaining work and known issues. | Remove the contradictory hint through `record-continuity`. |
|
|
793
|
+
| `CONTINUITY_INSPECT_PATH_MISSING` | An `inspectFirst` path is not present in the current target. | Reconcile the checkout and update the path hint. |
|
|
794
|
+
| `CONTINUITY_EMPTY_HINT_SET` | No operational hint was supplied. | Treat the result as informational and follow canonical `next`. |
|
|
795
|
+
|
|
760
796
|
### Symptom: Handoff Is Invalid or Tampered
|
|
761
797
|
|
|
762
|
-
#### Error Codes: `E_HANDOFF_INVALID`, `E_HANDOFF_STATE_UNAVAILABLE`, `E_HANDOFF_TAMPERED`, `E_HANDOFF_NOT_FOUND`
|
|
798
|
+
#### Error Codes: `E_HANDOFF_INVALID`, `E_HANDOFF_STATE_UNAVAILABLE`, `E_HANDOFF_TAMPERED`, `E_HANDOFF_NOT_FOUND`, `E_HANDOFF_STALE`, `E_HANDOFF_ALREADY_ACCEPTED`, `E_HANDOFF_ACCEPTANCE_INCONSISTENT`
|
|
763
799
|
|
|
764
800
|
#### What it means
|
|
765
801
|
|
|
766
802
|
The immutable handoff envelope is malformed, its state is unavailable, its
|
|
767
803
|
digest no longer matches, or the requested snapshot does not exist. A handoff
|
|
768
804
|
note is not delegation, authority, independent review evidence, or completion
|
|
769
|
-
evidence.
|
|
805
|
+
evidence. Acceptance is exactly-once operational receipt only. The current
|
|
806
|
+
repository branch and HEAD are checked directly, so clean committed checkout
|
|
807
|
+
drift can make a handoff stale even when changed paths are empty.
|
|
770
808
|
|
|
771
809
|
#### Safe recovery
|
|
772
810
|
|
|
@@ -777,6 +815,12 @@ forgeloop continuity --task <id> --json
|
|
|
777
815
|
forgeloop reconcile-continuity --task <id> --json
|
|
778
816
|
```
|
|
779
817
|
|
|
818
|
+
If `handoff-list` or `handoff-show` reports `INCONSISTENT`, inspect the
|
|
819
|
+
`reasonCodes` field. The commands validate the event ledger before projecting
|
|
820
|
+
acceptance and never turn an unreadable or invalid ledger into an empty ledger.
|
|
821
|
+
Repair the named ledger through the canonical protocol workflow; do not edit
|
|
822
|
+
`events.ndjson` by hand.
|
|
823
|
+
|
|
780
824
|
Use the last valid handoff or continuity only to focus inspection, then trust
|
|
781
825
|
the canonical task state and checkout. Never repair a handoff by editing or
|
|
782
826
|
deleting its JSON file.
|
|
@@ -890,6 +934,88 @@ forgeloop next --task <id> --json
|
|
|
890
934
|
|
|
891
935
|
---
|
|
892
936
|
|
|
937
|
+
### Symptom: Structural-quality evidence is missing or blocked
|
|
938
|
+
|
|
939
|
+
#### Error Codes: `E_STRUCTURAL_QUALITY_BASELINE_MISSING`, `E_STRUCTURAL_QUALITY_BASELINE_PHASE_INVALID`, `E_STRUCTURAL_QUALITY_PROVIDER_UNAVAILABLE`
|
|
940
|
+
|
|
941
|
+
This applies when structural quality is configured in `gate` mode but a valid
|
|
942
|
+
baseline or current provider observation is unavailable. `preflight` can be
|
|
943
|
+
ready before the baseline exists, but `PLANNED` cannot enter `EXECUTING` until
|
|
944
|
+
the baseline is captured. A missing provider is a visible limitation in
|
|
945
|
+
`observe` mode and a blocker in `gate` mode.
|
|
946
|
+
|
|
947
|
+
Inspect the persisted projection without launching a provider:
|
|
948
|
+
|
|
949
|
+
```bash
|
|
950
|
+
forgeloop quality-status --task <id> --json
|
|
951
|
+
forgeloop next --task <id> --json
|
|
952
|
+
```
|
|
953
|
+
|
|
954
|
+
Capture the baseline only in `PLANNED` after a valid preflight:
|
|
955
|
+
|
|
956
|
+
```bash
|
|
957
|
+
forgeloop quality-baseline --task <id> --json
|
|
958
|
+
```
|
|
959
|
+
|
|
960
|
+
Do not replace a baseline after execution begins. Install or upgrade Sentrux
|
|
961
|
+
only through a separately authorized, user-managed process.
|
|
962
|
+
|
|
963
|
+
### Symptom: Structural-quality verification reports a regression
|
|
964
|
+
|
|
965
|
+
#### Error Code: `E_STRUCTURAL_QUALITY_REGRESSION`
|
|
966
|
+
|
|
967
|
+
The current observation violated the configured aggregate, dimension, cycle, or
|
|
968
|
+
minimum policy. An improving aggregate can still fail when a dimension budget
|
|
969
|
+
or cycle rule fails. Inspect the evidence and follow the normal diagnosis and
|
|
970
|
+
correction lifecycle. Use the evaluation artifact reference returned by
|
|
971
|
+
`quality-verify` as evidence when recording the diagnosis.
|
|
972
|
+
|
|
973
|
+
```bash
|
|
974
|
+
forgeloop quality-status --task <id> --json
|
|
975
|
+
forgeloop next --task <id> --json
|
|
976
|
+
```
|
|
977
|
+
|
|
978
|
+
The evaluation's bottleneck, root-cause deltas, failed conditions, and artifact
|
|
979
|
+
reference are evidence. ForgeLoop does not auto-create a diagnosis and does not
|
|
980
|
+
accept a repeated hypothesis without new information.
|
|
981
|
+
|
|
982
|
+
### Symptom: Structural-quality provider is unavailable or invalid
|
|
983
|
+
|
|
984
|
+
#### Error Codes: `E_STRUCTURAL_QUALITY_PROVIDER_INVALID`, `E_STRUCTURAL_QUALITY_PROVIDER_VERSION_UNSUPPORTED`, `E_STRUCTURAL_QUALITY_PROVIDER_PROTOCOL_INVALID`, `E_STRUCTURAL_QUALITY_SCAN_FAILED`, `E_STRUCTURAL_QUALITY_TIMEOUT`, `E_STRUCTURAL_QUALITY_OUTPUT_LIMIT`
|
|
985
|
+
|
|
986
|
+
The provider boundary failed closed because detection, version, MCP protocol,
|
|
987
|
+
scan, timeout, or output limits were not satisfied. Raw MCP streams are never
|
|
988
|
+
persisted and partial provider output is never a passing observation.
|
|
989
|
+
|
|
990
|
+
Use the configured mode's semantics: `observe` records `NOT_OBSERVED`, while
|
|
991
|
+
`gate` records `BLOCKED` and keeps completion unavailable. Check the provider's
|
|
992
|
+
own installation and logs, then rerun the same ForgeLoop command after the
|
|
993
|
+
cause is understood. The project configuration cannot select an executable,
|
|
994
|
+
shell, arbitrary arguments, or a score override.
|
|
995
|
+
|
|
996
|
+
### Symptom: Structural-quality evidence is stale or incomparable
|
|
997
|
+
|
|
998
|
+
#### Error Codes: `E_STRUCTURAL_QUALITY_BASELINE_BINDING_MISMATCH`, `E_STRUCTURAL_QUALITY_EVALUATION_INCOMPARABLE`, `E_STRUCTURAL_QUALITY_EVIDENCE_STALE`, `E_STRUCTURAL_QUALITY_SOURCE_FINGERPRINT_UNAVAILABLE`
|
|
999
|
+
|
|
1000
|
+
The baseline and current evaluation no longer describe the same task inputs.
|
|
1001
|
+
Provider ID or version, policy, route, contract, scan scope, or
|
|
1002
|
+
`.sentrux/rules.toml` drift makes the comparison incomparable. A prior-cycle
|
|
1003
|
+
pass cannot satisfy a current-cycle gate. If source material cannot be read
|
|
1004
|
+
or contains an unsafe symlink, ForgeLoop returns
|
|
1005
|
+
`E_STRUCTURAL_QUALITY_SOURCE_FINGERPRINT_UNAVAILABLE` and does not persist a
|
|
1006
|
+
passing observation.
|
|
1007
|
+
|
|
1008
|
+
Inspect the bound artifacts and current projection:
|
|
1009
|
+
|
|
1010
|
+
```bash
|
|
1011
|
+
forgeloop quality-status --task <id> --json
|
|
1012
|
+
forgeloop audit --task <id> --json
|
|
1013
|
+
```
|
|
1014
|
+
|
|
1015
|
+
Resolve the legitimate drift through the normal task lifecycle. Do not edit a
|
|
1016
|
+
baseline or evaluation by hand, and do not use a bundle as a reason to bypass
|
|
1017
|
+
current-cycle validation.
|
|
1018
|
+
|
|
893
1019
|
## Stable Error and Reason Codes
|
|
894
1020
|
|
|
895
1021
|
<!-- BEGIN FORGELOOP GENERATED: public-error-codes -->
|
|
@@ -916,6 +1042,13 @@ forgeloop next --task <id> --json
|
|
|
916
1042
|
| `E_ACTION_STATE_MISMATCH` | Requested durable action transition is not part of the canonical state machine. | Inspect current action state with forgeloop action-show and use a legal transition; never edit action artifacts by hand. |
|
|
917
1043
|
| `E_ACTION_VERIFICATION_INVALID` | Verification evidence does not resolve to a canonical passed ForgeLoop artifact bound to this task and action. | Supply a canonical execution or check reference produced by run-check for this task; arbitrary strings fail closed. |
|
|
918
1044
|
| `E_ACTION_VERIFICATION_REQUIRED` | The action cannot reach VERIFIED through this surface; canonical independent postcondition evidence is required. | Run an independent verification check, then record it with forgeloop action-verify; exit code 0 alone is not verification. |
|
|
1045
|
+
| `E_ADVISORY_CONTEXT_OUTPUT_LIMIT` | Advisory context output exceeded the configured character or item limit. | Reduce query scope, limit items, or truncate oversized summaries at the provider. |
|
|
1046
|
+
| `E_ADVISORY_CONTEXT_PROVIDER_INVALID` | Advisory context provider configuration or interface implementation is invalid. | Use a provider implementing id, recall(input) with bounded query parameters; advisory context is optional. |
|
|
1047
|
+
| `E_ADVISORY_CONTEXT_PROVIDER_UNAVAILABLE` | Requested advisory context provider is not registered in runtime context. | Register the provider in runtime context before recall, or proceed without advisory context; provider failure never blocks canonical lifecycle. |
|
|
1048
|
+
| `E_ADVISORY_CONTEXT_QUERY_INVALID` | Advisory context query failed portable-context validation or exceeded budget. | Provide a bounded query free of control characters and secret-like values. |
|
|
1049
|
+
| `E_ADVISORY_CONTEXT_REQUEST_INVALID` | Advisory context recall budgets are not finite integer values within the supported request contract. | Provide finite integer limit, maxItemChars, maxTotalChars, and timeoutMs values; oversized valid values are clamped to documented maxima. |
|
|
1050
|
+
| `E_ADVISORY_CONTEXT_RESULT_INVALID` | Advisory context provider returned an invalid result structure. | Ensure provider returns items with string summary and optional title, sourceRef, observedAt, confidence. |
|
|
1051
|
+
| `E_ADVISORY_CONTEXT_TIMEOUT` | Advisory context recall exceeded its execution timeout. | Use a responsive provider or increase timeout within limits; advisory context is optional. |
|
|
919
1052
|
| `E_APPROVAL_ALREADY_RESOLVED` | Approval is one-time resolvable and has already been approved or rejected. | Request a new approval if another decision is required. |
|
|
920
1053
|
| `E_APPROVAL_INVALID` | Approval artifact is malformed or does not bind the required fingerprint tuple. | Request a new approval with forgeloop approval-request; never hand-edit approval artifacts. |
|
|
921
1054
|
| `E_APPROVAL_STALE` | Approval no longer matches the current action fingerprint, contract fingerprint, task revision, or capability. | Request and resolve a fresh approval against the current action revision. |
|
|
@@ -1004,8 +1137,12 @@ forgeloop next --task <id> --json
|
|
|
1004
1137
|
| `E_GATE_REQUIRED` | A ForgeLoop protocol validation or lifecycle condition was not satisfied. | Inspect the structured command result, correct the named artifact or prerequisite, then run forgeloop next --json. |
|
|
1005
1138
|
| `E_GATE_STALE` | Referenced gate artifact changed after approval. | Update artifact SHA-256 in gate file. |
|
|
1006
1139
|
| `E_GATE_UNVERIFIED` | A ForgeLoop protocol validation or lifecycle condition was not satisfied. | Inspect the structured command result, correct the named artifact or prerequisite, then run forgeloop next --json. |
|
|
1140
|
+
| `E_HANDOFF_ACCEPTANCE_INCONSISTENT` | Handoff acceptance disagrees with task event ledger history. | Verify ledger integrity and require a preceding valid HANDOFF_CREATED event. |
|
|
1141
|
+
| `E_HANDOFF_ACCEPTANCE_UNBOUND` | Handoff snapshot lacks required workStateFingerprint binding. | Create a fresh handoff from the current ForgeLoop version before accepting it. |
|
|
1142
|
+
| `E_HANDOFF_ALREADY_ACCEPTED` | Handoff was already accepted by a different consumer. | Create a new handoff for the new consumer; do not manually edit acceptance events. |
|
|
1007
1143
|
| `E_HANDOFF_INVALID` | A ForgeLoop boundary, artifact, provider, or attestation validation condition was not satisfied. | Inspect the structured command result, correct the named boundary or artifact, then retry the canonical command. |
|
|
1008
1144
|
| `E_HANDOFF_NOT_FOUND` | A ForgeLoop boundary, artifact, provider, or attestation validation condition was not satisfied. | Inspect the structured command result, correct the named boundary or artifact, then retry the canonical command. |
|
|
1145
|
+
| `E_HANDOFF_STALE` | Handoff snapshot has drifted from the current canonical task state or repository. | Create a new fresh handoff from the current task state instead of accepting a stale snapshot. |
|
|
1009
1146
|
| `E_HANDOFF_STATE_UNAVAILABLE` | A ForgeLoop boundary, artifact, provider, or attestation validation condition was not satisfied. | Inspect the structured command result, correct the named boundary or artifact, then retry the canonical command. |
|
|
1010
1147
|
| `E_HANDOFF_TAMPERED` | A ForgeLoop boundary, artifact, provider, or attestation validation condition was not satisfied. | Inspect the structured command result, correct the named boundary or artifact, then retry the canonical command. |
|
|
1011
1148
|
| `E_HYPOTHESIS_DISPOSITION_EVIDENCE_INVALID` | Hypothesis disposition evidence references do not resolve to checks of the active cycle. | Reference at least one check ID recorded in the active verification cycle. |
|
|
@@ -1038,6 +1175,7 @@ forgeloop next --task <id> --json
|
|
|
1038
1175
|
| `E_POLICY_PROOF_STALE` | Mutation verification proof is stale due to checker or fixture modifications. | Re-run forgeloop rule-verify to refresh mutation proof. |
|
|
1039
1176
|
| `E_POLICY_SNAPSHOT_WRITE_FAILED` | Failed to persist task policy snapshot during preflight. | Ensure the target task directory is writable and repair filesystem permissions. |
|
|
1040
1177
|
| `E_POLICY_WEAKENING` | Policy rules were weakened during task execution without explicit authority. | Restore the original policy configuration. |
|
|
1178
|
+
| `E_PORTABLE_CONTEXT_INVALID` | Text or object failed portable-context safety, character, or secret limits. | Ensure text is bounded, contains no control characters, and contains no secret-like values. |
|
|
1041
1179
|
| `E_PREFLIGHT_EVENT_MISSING` | A ForgeLoop protocol validation or lifecycle condition was not satisfied. | Inspect the structured command result, correct the named artifact or prerequisite, then run forgeloop next --json. |
|
|
1042
1180
|
| `E_PREFLIGHT_GATES_STALE` | A ForgeLoop protocol validation or lifecycle condition was not satisfied. | Inspect the structured command result, correct the named artifact or prerequisite, then run forgeloop next --json. |
|
|
1043
1181
|
| `E_PREFLIGHT_GATE_EVENT_MISSING` | A ForgeLoop protocol validation or lifecycle condition was not satisfied. | Inspect the structured command result, correct the named artifact or prerequisite, then run forgeloop next --json. |
|
|
@@ -1090,6 +1228,27 @@ forgeloop next --task <id> --json
|
|
|
1090
1228
|
| `E_STATE_REVALIDATION_REQUIRED` | The work-state checkpoint must be revalidated before the lifecycle can continue. | Run forgeloop reconcile-closure for externally satisfied EXECUTING tasks, or inspect the freshness reasons for other drift. |
|
|
1091
1229
|
| `E_STATE_TASK_MISMATCH` | A ForgeLoop protocol validation or lifecycle condition was not satisfied. | Inspect the structured command result, correct the named artifact or prerequisite, then run forgeloop next --json. |
|
|
1092
1230
|
| `E_STRATEGY_OSCILLATION` | Correction history oscillates between previously exhausted strategies without new information. | Gather a genuinely new observation or test a materially different falsifiable hypothesis. |
|
|
1231
|
+
| `E_STRUCTURAL_QUALITY_BASELINE_BINDING_MISMATCH` | Structural-quality evidence did not satisfy its provider, artifact, comparison, or lifecycle boundary. | Reconcile contract, route, policy, scope, provider, or rules drift before using the baseline. |
|
|
1232
|
+
| `E_STRUCTURAL_QUALITY_BASELINE_EXISTS` | Structural-quality evidence did not satisfy its provider, artifact, comparison, or lifecycle boundary. | Use the existing immutable baseline or request --replace while the task is still before EXECUTING. |
|
|
1233
|
+
| `E_STRUCTURAL_QUALITY_BASELINE_MISSING` | Structural-quality evidence did not satisfy its provider, artifact, comparison, or lifecycle boundary. | Run forgeloop quality-baseline --task <id> after PLANNED and before EXECUTING. |
|
|
1234
|
+
| `E_STRUCTURAL_QUALITY_BASELINE_PHASE_INVALID` | Structural-quality evidence did not satisfy its provider, artifact, comparison, or lifecycle boundary. | Baseline replacement is forbidden after EXECUTING; repair the current task against its original baseline. |
|
|
1235
|
+
| `E_STRUCTURAL_QUALITY_CONFIGURATION_INVALID` | Structural-quality evidence did not satisfy its provider, artifact, comparison, or lifecycle boundary. | Correct structuralQuality mode, provider ID, budgets, floors, or optimization limits in .forgeloop/config.json. |
|
|
1236
|
+
| `E_STRUCTURAL_QUALITY_EVALUATION_INCOMPARABLE` | Structural-quality evidence did not satisfy its provider, artifact, comparison, or lifecycle boundary. | Restore the baseline provider/version/rules/policy/scope bindings and rerun quality-verify. |
|
|
1237
|
+
| `E_STRUCTURAL_QUALITY_EVIDENCE_STALE` | Structural-quality evidence did not satisfy its provider, artifact, comparison, or lifecycle boundary. | Rerun quality-verify in the active verification cycle and refresh completion through the canonical receipt pipeline. |
|
|
1238
|
+
| `E_STRUCTURAL_QUALITY_MEASUREMENT_MODEL_MISMATCH` | Structural-quality evidence did not satisfy its provider, artifact, comparison, or lifecycle boundary. | Use a compatible measurement model and provider across baseline and evaluation observations. |
|
|
1239
|
+
| `E_STRUCTURAL_QUALITY_OBSERVATION_EPOCH_STALE` | Structural-quality evidence did not satisfy its provider, artifact, comparison, or lifecycle boundary. | Rerun quality-verify under the active task epoch without concurrent state mutations. |
|
|
1240
|
+
| `E_STRUCTURAL_QUALITY_OUTPUT_LIMIT` | Structural-quality evidence did not satisfy its provider, artifact, comparison, or lifecycle boundary. | Reduce provider output or diagnostics; the 2 MiB combined process-output limit is fail-closed. |
|
|
1241
|
+
| `E_STRUCTURAL_QUALITY_PROJECTION_INCOMPLETE` | Structural-quality evidence did not satisfy its provider, artifact, comparison, or lifecycle boundary. | Rerun quality-verify to reconcile and project the canonical check from the existing evaluation artifact. |
|
|
1242
|
+
| `E_STRUCTURAL_QUALITY_PROVIDER_INVALID` | Structural-quality evidence did not satisfy its provider, artifact, comparison, or lifecycle boundary. | Use a provider implementing id, detect(input), and scan(input) with the documented normalized boundary. |
|
|
1243
|
+
| `E_STRUCTURAL_QUALITY_PROVIDER_PROTOCOL_INVALID` | Structural-quality evidence did not satisfy its provider, artifact, comparison, or lifecycle boundary. | Repair the provider MCP handshake or response shape; malformed external data cannot become evidence. |
|
|
1244
|
+
| `E_STRUCTURAL_QUALITY_PROVIDER_TOOL_CONTRACT_INVALID` | Structural-quality evidence did not satisfy its provider, artifact, comparison, or lifecycle boundary. | Ensure the provider exposes the required scan and health tool argument schemas. |
|
|
1245
|
+
| `E_STRUCTURAL_QUALITY_PROVIDER_UNAVAILABLE` | Structural-quality evidence did not satisfy its provider, artifact, comparison, or lifecycle boundary. | Install or expose the requested provider outside ForgeLoop, or use observe mode and record the limitation; ForgeLoop never auto-installs it. |
|
|
1246
|
+
| `E_STRUCTURAL_QUALITY_PROVIDER_VERSION_UNSUPPORTED` | Structural-quality evidence did not satisfy its provider, artifact, comparison, or lifecycle boundary. | Use a verified Sentrux version (0.5.5, 0.5.6, or 0.5.7), or select a compatible provider through trusted runtime context. |
|
|
1247
|
+
| `E_STRUCTURAL_QUALITY_REGRESSION` | Structural-quality evidence did not satisfy its provider, artifact, comparison, or lifecycle boundary. | Use the bottleneck and root-cause deltas to record an evidence-backed diagnosis, correct within scope, and verify a new cycle. |
|
|
1248
|
+
| `E_STRUCTURAL_QUALITY_SCAN_FAILED` | Structural-quality evidence did not satisfy its provider, artifact, comparison, or lifecycle boundary. | Inspect the provider failure and rerun quality-baseline or quality-verify after the external analyzer is healthy. |
|
|
1249
|
+
| `E_STRUCTURAL_QUALITY_SOURCE_DRIFT` | Structural-quality evidence did not satisfy its provider, artifact, comparison, or lifecycle boundary. | Ensure the worktree is not mutated during provider observation and rerun quality-baseline or quality-verify. |
|
|
1250
|
+
| `E_STRUCTURAL_QUALITY_SOURCE_FINGERPRINT_UNAVAILABLE` | Structural-quality evidence did not satisfy its provider, artifact, comparison, or lifecycle boundary. | Repair unreadable or unsafe source material before structural-quality evidence can be trusted. |
|
|
1251
|
+
| `E_STRUCTURAL_QUALITY_TIMEOUT` | Structural-quality evidence did not satisfy its provider, artifact, comparison, or lifecycle boundary. | Use a responsive provider or a bounded timeout within the supported limit; never promote a timed-out scan. |
|
|
1093
1252
|
| `E_TASK_ALREADY_EXISTS` | A ForgeLoop protocol validation or lifecycle condition was not satisfied. | Inspect the structured command result, correct the named artifact or prerequisite, then run forgeloop next --json. |
|
|
1094
1253
|
| `E_TASK_ALREADY_RECOVERED` | The task already has active durable recovered state. | Inspect the existing recovery metadata; use task-resume to reacquire claims or leave the task recovered. |
|
|
1095
1254
|
| `E_TASK_AMBIGUOUS` | Multiple tasks exist in the project but no task selector was provided. | Select a task explicitly using --task <id> or FORGELOOP_TASK=<id>. |
|
|
@@ -93,6 +93,66 @@ commands in [`EXECUTION_PROFILE_BENCHMARKS.md`](./EXECUTION_PROFILE_BENCHMARKS.m
|
|
|
93
93
|
The benchmark source policy accepts provider or host observations only and
|
|
94
94
|
keeps unavailable or non-comparable measurements out of efficiency claims.
|
|
95
95
|
|
|
96
|
+
### Advisory Context & Handoff Acceptance
|
|
97
|
+
|
|
98
|
+
Capability negotiation is feature-first: `canonicalHandoffs` is v2 and
|
|
99
|
+
`advisoryContextProviders` is v1. These capability-family versions are
|
|
100
|
+
independent of Protocol v1, schema v1, and Integration API v1. The programmatic
|
|
101
|
+
integration API exposes lazy advisory context querying and exactly-once handoff
|
|
102
|
+
acceptance:
|
|
103
|
+
|
|
104
|
+
```javascript
|
|
105
|
+
import {
|
|
106
|
+
createForgeLoopContext,
|
|
107
|
+
recallAdvisoryContext,
|
|
108
|
+
acceptCanonicalHandoff,
|
|
109
|
+
resolveHandoffAcceptance,
|
|
110
|
+
} from "@cassiomc1/forgeloop/integration";
|
|
111
|
+
|
|
112
|
+
// 1. Register host-provided advisory context
|
|
113
|
+
const runtimeContext = createForgeLoopContext({
|
|
114
|
+
advisoryContextProviders: {
|
|
115
|
+
"host-memory": {
|
|
116
|
+
id: "host-memory",
|
|
117
|
+
recall: async ({ query }) => ({ items: [...] }),
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// 2. Explicitly query advisory context (strictly non-evidence, non-executable)
|
|
123
|
+
const advisoryResult = await recallAdvisoryContext({
|
|
124
|
+
target: ".",
|
|
125
|
+
taskId: "task-1",
|
|
126
|
+
providerName: "host-memory",
|
|
127
|
+
query: "authentication tokens",
|
|
128
|
+
runtimeContext,
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// 3. Exactly-once handoff acceptance
|
|
132
|
+
const acceptance = await acceptCanonicalHandoff(".", {
|
|
133
|
+
taskId: "task-1",
|
|
134
|
+
handoffId: "handoff-001",
|
|
135
|
+
consumerId: "agent-session-42",
|
|
136
|
+
harness: "cursor",
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
See [`ADVISORY_CONTEXT.md`](./ADVISORY_CONTEXT.md) for full trust boundary specifications, portable text sanitization rules, and budget enforcement.
|
|
141
|
+
|
|
142
|
+
Advisory recall budgets are normalized before provider dispatch: valid oversized
|
|
143
|
+
integer requests are clamped, while invalid finite/integer/minimum types fail
|
|
144
|
+
with `E_ADVISORY_CONTEXT_REQUEST_INVALID` before provider lookup. The registry
|
|
145
|
+
key and resolved provider `id` must match. Handoff acceptance independently
|
|
146
|
+
checks canonical state and the current repository branch/HEAD, and remains an
|
|
147
|
+
exactly-once operational receipt with no evidence, claim transfer, or authority.
|
|
148
|
+
|
|
149
|
+
Advisory context is not a canonical resource unless a future explicitly
|
|
150
|
+
versioned resource introduces one. `protocol-info` advertises the optional
|
|
151
|
+
provider capability, but the stock CLI never recalls it automatically and
|
|
152
|
+
there is no stock `context-recall` command. A consumer that only understands
|
|
153
|
+
`canonicalHandoffs` v1 may disable only the handoff-specific UI while retaining
|
|
154
|
+
the rest of Protocol v1 functionality.
|
|
155
|
+
|
|
96
156
|
## Consumers
|
|
97
157
|
|
|
98
158
|
| Surface | Entry |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cassiomc1/forgeloop",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
4
4
|
"description": "Portable, verifiable engineering protocol for AI coding environments and developer workflows",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -51,10 +51,12 @@
|
|
|
51
51
|
"docs/assets/diagrams",
|
|
52
52
|
"docs/GETTING_STARTED.md",
|
|
53
53
|
"docs/CROSS_HARNESS_CONTINUITY.md",
|
|
54
|
+
"docs/ADVISORY_CONTEXT.md",
|
|
54
55
|
"docs/CLI_REFERENCE.md",
|
|
55
56
|
"docs/ARTIFACT_REFERENCE.md",
|
|
56
57
|
"docs/TROUBLESHOOTING.md",
|
|
57
58
|
"docs/RECIPES.md",
|
|
59
|
+
"docs/STRUCTURAL_QUALITY.md",
|
|
58
60
|
"docs/DOCUMENTATION_GUIDE.md",
|
|
59
61
|
"docs/RELEASE_CHECKLIST.md",
|
|
60
62
|
"scripts/CI_VALIDATORS.md",
|
|
@@ -68,6 +70,7 @@
|
|
|
68
70
|
"docs/PLATFORM_ADAPTERS.md",
|
|
69
71
|
"docs/AGENT_PROTOCOL_SUMMARY.md",
|
|
70
72
|
"docs/EXECUTION_PROFILE_BENCHMARKS.md",
|
|
73
|
+
"docs/KNOWLEDGE_SOURCES.md",
|
|
71
74
|
"scripts/generate-shell-completions.mjs",
|
|
72
75
|
"scripts/generate-agent-protocol-summary.mjs",
|
|
73
76
|
"scripts/run-execution-profile-benchmarks.mjs",
|
|
@@ -12,6 +12,28 @@
|
|
|
12
12
|
"policy": { "type": "string", "minLength": 1 },
|
|
13
13
|
"requiredGates": { "type": "array", "items": { "type": "string", "minLength": 1 } },
|
|
14
14
|
"requiredEvidence": { "type": "array", "items": { "type": "string", "minLength": 1 } },
|
|
15
|
+
"structuralQuality": {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"additionalProperties": false,
|
|
18
|
+
"properties": {
|
|
19
|
+
"mode": { "enum": ["off", "observe", "gate"] },
|
|
20
|
+
"provider": { "type": "string", "pattern": "^[a-z][a-z0-9-]{0,63}$" },
|
|
21
|
+
"maxRegressionPoints": { "type": "integer", "minimum": 0, "maximum": 10000 },
|
|
22
|
+
"dimensionBudgets": { "$ref": "#/$defs/dimensionMap" },
|
|
23
|
+
"forbidNewCycles": { "type": "boolean" },
|
|
24
|
+
"minQualitySignal": { "oneOf": [{ "type": "integer", "minimum": 0, "maximum": 10000 }, { "type": "null" }] },
|
|
25
|
+
"minimums": { "$ref": "#/$defs/minimumMap" },
|
|
26
|
+
"optimization": {
|
|
27
|
+
"type": "object",
|
|
28
|
+
"additionalProperties": false,
|
|
29
|
+
"properties": {
|
|
30
|
+
"mode": { "enum": ["off", "bounded"] },
|
|
31
|
+
"maxExtraEvaluations": { "type": "integer", "minimum": 0, "maximum": 2 },
|
|
32
|
+
"minGainPoints": { "type": "integer", "minimum": 1, "maximum": 10000 }
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
},
|
|
15
37
|
"verification": {
|
|
16
38
|
"type": "object",
|
|
17
39
|
"required": ["checkers"],
|
|
@@ -69,5 +91,29 @@
|
|
|
69
91
|
}
|
|
70
92
|
}
|
|
71
93
|
},
|
|
94
|
+
"$defs": {
|
|
95
|
+
"dimensionMap": {
|
|
96
|
+
"type": "object",
|
|
97
|
+
"properties": {
|
|
98
|
+
"modularity": { "oneOf": [{ "type": "integer", "minimum": 0, "maximum": 10000 }, { "type": "null" }] },
|
|
99
|
+
"acyclicity": { "oneOf": [{ "type": "integer", "minimum": 0, "maximum": 10000 }, { "type": "null" }] },
|
|
100
|
+
"depth": { "oneOf": [{ "type": "integer", "minimum": 0, "maximum": 10000 }, { "type": "null" }] },
|
|
101
|
+
"equality": { "oneOf": [{ "type": "integer", "minimum": 0, "maximum": 10000 }, { "type": "null" }] },
|
|
102
|
+
"redundancy": { "oneOf": [{ "type": "integer", "minimum": 0, "maximum": 10000 }, { "type": "null" }] }
|
|
103
|
+
},
|
|
104
|
+
"additionalProperties": false
|
|
105
|
+
},
|
|
106
|
+
"minimumMap": {
|
|
107
|
+
"type": "object",
|
|
108
|
+
"properties": {
|
|
109
|
+
"modularity": { "type": "integer", "minimum": 0, "maximum": 10000 },
|
|
110
|
+
"acyclicity": { "type": "integer", "minimum": 0, "maximum": 10000 },
|
|
111
|
+
"depth": { "type": "integer", "minimum": 0, "maximum": 10000 },
|
|
112
|
+
"equality": { "type": "integer", "minimum": 0, "maximum": 10000 },
|
|
113
|
+
"redundancy": { "type": "integer", "minimum": 0, "maximum": 10000 }
|
|
114
|
+
},
|
|
115
|
+
"additionalProperties": false
|
|
116
|
+
}
|
|
117
|
+
},
|
|
72
118
|
"additionalProperties": false
|
|
73
119
|
}
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"phase": { "type": "string", "minLength": 1 },
|
|
28
28
|
"revision": { "type": "integer", "minimum": 0 },
|
|
29
29
|
"verificationCycle": { "type": "integer", "minimum": 1 },
|
|
30
|
+
"workStateFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
|
|
30
31
|
"contractFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
|
|
31
32
|
"routeFingerprint": { "oneOf": [{ "type": "string", "pattern": "^[a-f0-9]{64}$" }, { "type": "null" }] },
|
|
32
33
|
"repositoryFingerprint": { "type": "object" },
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
"errors": { "type": "array", "items": { "type": "object" } },
|
|
18
18
|
"fingerprints": { "type": "object" },
|
|
19
19
|
"sources": { "type": "object" },
|
|
20
|
-
"policy": { "type": "object" }
|
|
20
|
+
"policy": { "type": "object" },
|
|
21
|
+
"structuralQuality": { "type": "object" }
|
|
21
22
|
},
|
|
22
23
|
"additionalProperties": false
|
|
23
24
|
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "forgeloop://schemas/structural-quality.schema.json",
|
|
4
|
+
"title": "ForgeLoop structural quality evidence",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["schemaVersion", "protocolVersion", "role", "taskId", "capturedAt", "verificationCycle", "attempt", "bindings", "provider", "scope", "status", "reasonCodes"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"schemaVersion": { "const": 1 },
|
|
9
|
+
"protocolVersion": { "const": 1 },
|
|
10
|
+
"role": { "enum": ["BASELINE", "EVALUATION"] },
|
|
11
|
+
"taskId": { "type": "string", "minLength": 1 },
|
|
12
|
+
"capturedAt": { "type": "string", "minLength": 1 },
|
|
13
|
+
"verificationCycle": { "oneOf": [{ "type": "integer", "minimum": 1 }, { "type": "null" }] },
|
|
14
|
+
"attempt": { "type": "integer", "minimum": 1 },
|
|
15
|
+
"status": { "enum": ["PASS", "FAIL", "BLOCKED", "NOT_OBSERVED"] },
|
|
16
|
+
"reasonCodes": { "type": "array", "items": { "type": "string", "minLength": 1 } },
|
|
17
|
+
"errorCode": { "oneOf": [{ "type": "string", "minLength": 1 }, { "type": "null" }] },
|
|
18
|
+
"baselineSignal": { "oneOf": [{ "type": "integer", "minimum": 0, "maximum": 10000 }, { "type": "null" }] },
|
|
19
|
+
"currentSignal": { "oneOf": [{ "type": "integer", "minimum": 0, "maximum": 10000 }, { "type": "null" }] },
|
|
20
|
+
"bindings": {
|
|
21
|
+
"type": "object",
|
|
22
|
+
"required": ["contractFingerprint", "routeFingerprint", "policyFingerprint", "scopeFingerprint"],
|
|
23
|
+
"properties": {
|
|
24
|
+
"contractFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
|
|
25
|
+
"routeFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
|
|
26
|
+
"policyFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
|
|
27
|
+
"scopeFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
|
|
28
|
+
"baselineFingerprint": { "oneOf": [{ "type": "string", "pattern": "^[a-f0-9]{64}$" }, { "type": "null" }] },
|
|
29
|
+
"sourceMaterialFingerprint": { "oneOf": [{ "type": "string", "pattern": "^[a-f0-9]{64}$" }, { "type": "null" }] },
|
|
30
|
+
"stateRevision": { "type": "integer", "minimum": 0 }
|
|
31
|
+
},
|
|
32
|
+
"additionalProperties": false
|
|
33
|
+
},
|
|
34
|
+
"sourceObservation": {
|
|
35
|
+
"type": "object",
|
|
36
|
+
"required": ["beforeFingerprint", "afterFingerprint", "stable"],
|
|
37
|
+
"properties": {
|
|
38
|
+
"beforeFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
|
|
39
|
+
"afterFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
|
|
40
|
+
"stable": { "type": "boolean" }
|
|
41
|
+
},
|
|
42
|
+
"additionalProperties": false
|
|
43
|
+
},
|
|
44
|
+
"provider": {
|
|
45
|
+
"type": "object",
|
|
46
|
+
"required": ["id", "version", "transport", "executionMode"],
|
|
47
|
+
"properties": {
|
|
48
|
+
"id": { "type": "string", "minLength": 1 },
|
|
49
|
+
"version": { "oneOf": [{ "type": "string", "minLength": 1 }, { "type": "null" }] },
|
|
50
|
+
"transport": { "type": "string", "minLength": 1 },
|
|
51
|
+
"executionMode": { "type": "string", "minLength": 1 },
|
|
52
|
+
"measurementModel": { "type": "string", "minLength": 1 },
|
|
53
|
+
"compatibilityKey": { "oneOf": [{ "type": "string", "minLength": 1 }, { "type": "null" }] }
|
|
54
|
+
},
|
|
55
|
+
"additionalProperties": false
|
|
56
|
+
},
|
|
57
|
+
"detection": {
|
|
58
|
+
"type": "object",
|
|
59
|
+
"required": ["available", "providerId", "transport", "reasonCode"],
|
|
60
|
+
"properties": {
|
|
61
|
+
"available": { "type": "boolean" },
|
|
62
|
+
"providerId": { "type": "string", "minLength": 1 },
|
|
63
|
+
"providerVersion": { "oneOf": [{ "type": "string", "minLength": 1 }, { "type": "null" }] },
|
|
64
|
+
"transport": { "type": "string", "minLength": 1 },
|
|
65
|
+
"measurementModel": { "type": "string", "minLength": 1 },
|
|
66
|
+
"compatibilityKey": { "oneOf": [{ "type": "string", "minLength": 1 }, { "type": "null" }] },
|
|
67
|
+
"reasonCode": { "oneOf": [{ "type": "string", "minLength": 1 }, { "type": "null" }] }
|
|
68
|
+
},
|
|
69
|
+
"additionalProperties": false
|
|
70
|
+
},
|
|
71
|
+
"scope": {
|
|
72
|
+
"type": "object",
|
|
73
|
+
"required": ["kind", "projectRoot"],
|
|
74
|
+
"properties": {
|
|
75
|
+
"kind": { "const": "PROJECT" },
|
|
76
|
+
"projectRoot": { "const": "." },
|
|
77
|
+
"providerConfigFingerprint": { "oneOf": [{ "type": "string", "pattern": "^[a-f0-9]{64}$" }, { "type": "null" }] },
|
|
78
|
+
"architectureRulesFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" }
|
|
79
|
+
},
|
|
80
|
+
"additionalProperties": false
|
|
81
|
+
},
|
|
82
|
+
"snapshot": { "$ref": "#/$defs/snapshot" },
|
|
83
|
+
"comparison": { "$ref": "#/$defs/comparison" }
|
|
84
|
+
},
|
|
85
|
+
"additionalProperties": false,
|
|
86
|
+
"$defs": {
|
|
87
|
+
"rootCause": {
|
|
88
|
+
"type": "object",
|
|
89
|
+
"required": ["score", "raw"],
|
|
90
|
+
"properties": {
|
|
91
|
+
"score": { "type": "integer", "minimum": 0, "maximum": 10000 },
|
|
92
|
+
"raw": { "type": "number" }
|
|
93
|
+
},
|
|
94
|
+
"additionalProperties": false
|
|
95
|
+
},
|
|
96
|
+
"snapshot": {
|
|
97
|
+
"type": "object",
|
|
98
|
+
"required": ["qualitySignal", "bottleneck", "rootCauses", "statistics", "diagnostics"],
|
|
99
|
+
"properties": {
|
|
100
|
+
"qualitySignal": { "type": "integer", "minimum": 0, "maximum": 10000 },
|
|
101
|
+
"bottleneck": { "enum": ["modularity", "acyclicity", "depth", "equality", "redundancy"] },
|
|
102
|
+
"rootCauses": {
|
|
103
|
+
"type": "object",
|
|
104
|
+
"required": ["modularity", "acyclicity", "depth", "equality", "redundancy"],
|
|
105
|
+
"properties": {
|
|
106
|
+
"modularity": { "$ref": "#/$defs/rootCause" },
|
|
107
|
+
"acyclicity": { "$ref": "#/$defs/rootCause" },
|
|
108
|
+
"depth": { "$ref": "#/$defs/rootCause" },
|
|
109
|
+
"equality": { "$ref": "#/$defs/rootCause" },
|
|
110
|
+
"redundancy": { "$ref": "#/$defs/rootCause" }
|
|
111
|
+
},
|
|
112
|
+
"additionalProperties": false
|
|
113
|
+
},
|
|
114
|
+
"statistics": {
|
|
115
|
+
"type": "object",
|
|
116
|
+
"required": ["files", "lines", "importEdges", "crossModuleEdges"],
|
|
117
|
+
"properties": {
|
|
118
|
+
"files": { "oneOf": [{ "type": "integer", "minimum": 0 }, { "type": "null" }] },
|
|
119
|
+
"lines": { "oneOf": [{ "type": "integer", "minimum": 0 }, { "type": "null" }] },
|
|
120
|
+
"importEdges": { "oneOf": [{ "type": "integer", "minimum": 0 }, { "type": "null" }] },
|
|
121
|
+
"crossModuleEdges": { "oneOf": [{ "type": "integer", "minimum": 0 }, { "type": "null" }] }
|
|
122
|
+
},
|
|
123
|
+
"additionalProperties": false
|
|
124
|
+
},
|
|
125
|
+
"diagnostics": { "oneOf": [{ "type": "object" }, { "type": "null" }] }
|
|
126
|
+
},
|
|
127
|
+
"additionalProperties": false
|
|
128
|
+
},
|
|
129
|
+
"comparison": {
|
|
130
|
+
"type": "object",
|
|
131
|
+
"required": ["comparable", "qualityDelta", "rootCauseDeltas", "status", "reasonCodes"],
|
|
132
|
+
"properties": {
|
|
133
|
+
"comparable": { "type": "boolean" },
|
|
134
|
+
"qualityDelta": { "oneOf": [{ "type": "integer" }, { "type": "null" }] },
|
|
135
|
+
"rootCauseDeltas": { "type": "object" },
|
|
136
|
+
"failedConditions": { "type": "array", "items": { "type": "string", "minLength": 1 } },
|
|
137
|
+
"status": { "enum": ["PASS", "FAIL", "BLOCKED", "NOT_OBSERVED"] },
|
|
138
|
+
"reasonCodes": { "type": "array", "items": { "type": "string", "minLength": 1 } }
|
|
139
|
+
},
|
|
140
|
+
"additionalProperties": false
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
"allOf": [
|
|
144
|
+
{
|
|
145
|
+
"if": { "properties": { "role": { "const": "BASELINE" } } },
|
|
146
|
+
"then": {
|
|
147
|
+
"required": ["sourceObservation"],
|
|
148
|
+
"properties": {
|
|
149
|
+
"bindings": {
|
|
150
|
+
"required": ["sourceMaterialFingerprint"],
|
|
151
|
+
"properties": {
|
|
152
|
+
"sourceMaterialFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" }
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
"sourceObservation": { "properties": { "stable": { "const": true } } }
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"if": { "properties": { "status": { "enum": ["PASS", "FAIL"] } } },
|
|
161
|
+
"then": {
|
|
162
|
+
"required": ["sourceObservation"],
|
|
163
|
+
"properties": {
|
|
164
|
+
"bindings": {
|
|
165
|
+
"required": ["sourceMaterialFingerprint"],
|
|
166
|
+
"properties": {
|
|
167
|
+
"sourceMaterialFingerprint": { "type": "string", "pattern": "^[a-f0-9]{64}$" }
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
"sourceObservation": { "properties": { "stable": { "const": true } } }
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
]
|
|
175
|
+
}
|
|
@@ -26,6 +26,26 @@ export function unreleasedSection(changelog) {
|
|
|
26
26
|
return nextHeading ? rest.slice(0, nextHeading.index) : rest;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
export function releaseSection(changelog) {
|
|
30
|
+
const heading = /^##[ \t]+(v?\d+\.\d+\.\d+)(?:[ \t]+-[^\r\n]*)?$/imu.exec(changelog);
|
|
31
|
+
if (!heading) return null;
|
|
32
|
+
const rest = changelog.slice(heading.index + heading[0].length);
|
|
33
|
+
const nextHeading = /^##\s/imu.exec(rest);
|
|
34
|
+
return {
|
|
35
|
+
version: heading[1].replace(/^v/u, ""),
|
|
36
|
+
section: nextHeading ? rest.slice(0, nextHeading.index) : rest,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function compareVersions(left, right) {
|
|
41
|
+
const leftParts = left.split(".").map(Number);
|
|
42
|
+
const rightParts = right.split(".").map(Number);
|
|
43
|
+
for (let index = 0; index < leftParts.length; index += 1) {
|
|
44
|
+
if (leftParts[index] !== rightParts[index]) return leftParts[index] - rightParts[index];
|
|
45
|
+
}
|
|
46
|
+
return 0;
|
|
47
|
+
}
|
|
48
|
+
|
|
29
49
|
export function stripHtmlComments(value) {
|
|
30
50
|
const chunks = [];
|
|
31
51
|
let cursor = 0;
|
|
@@ -55,11 +75,15 @@ export function hasMeaningfulUnreleasedContent(section) {
|
|
|
55
75
|
|
|
56
76
|
export function checkChangelogFreshness({ changelog, tags = [], commitsSinceLatestTag = 0, allowEmpty = false } = {}) {
|
|
57
77
|
const latestTag = latestReleaseTag(tags);
|
|
58
|
-
const
|
|
78
|
+
const unreleased = unreleasedSection(changelog);
|
|
79
|
+
const pendingRelease = releaseSection(changelog);
|
|
80
|
+
const pendingReleaseIsNewer = pendingRelease &&
|
|
81
|
+
(!latestTag || compareVersions(pendingRelease.version, latestTag.replace(/^v/u, "")) > 0);
|
|
82
|
+
const section = unreleased || (pendingReleaseIsNewer ? pendingRelease.section : "");
|
|
59
83
|
const hasChanges = commitsSinceLatestTag > 0;
|
|
60
84
|
const populated = hasMeaningfulUnreleasedContent(section);
|
|
61
85
|
const ok = allowEmpty || !hasChanges || populated;
|
|
62
|
-
return { ok, latestTag, hasChanges, populated, section };
|
|
86
|
+
return { ok, latestTag, hasChanges, populated, section, pendingRelease };
|
|
63
87
|
}
|
|
64
88
|
|
|
65
89
|
async function run() {
|
|
@@ -71,7 +95,7 @@ async function run() {
|
|
|
71
95
|
: Number(git(["rev-list", "--count", "HEAD"]));
|
|
72
96
|
const result = checkChangelogFreshness({ changelog, tags, commitsSinceLatestTag, allowEmpty: process.argv.includes("--allow-empty") });
|
|
73
97
|
if (!result.ok) {
|
|
74
|
-
console.error(`CHANGELOG.md has changes since ${result.latestTag ?? "the initial commit"} but
|
|
98
|
+
console.error(`CHANGELOG.md has changes since ${result.latestTag ?? "the initial commit"} but no populated Unreleased or pending release section exists.`);
|
|
75
99
|
process.exitCode = 1;
|
|
76
100
|
return;
|
|
77
101
|
}
|