@ontrails/trails 1.0.0-beta.39 → 1.0.0-beta.41
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/CHANGELOG.md +88 -0
- package/README.md +1 -1
- package/package.json +17 -17
- package/src/app.ts +4 -4
- package/src/cli.ts +1 -1
- package/src/completions.ts +1 -1
- package/src/mcp-app.ts +2 -2
- package/src/regrade/history.ts +148 -38
- package/src/regrade/plan-artifact.ts +31 -11
- package/src/release/native-bun-registry.ts +12 -1
- package/src/release/policy.ts +4 -1
- package/src/retired-topo-command.ts +1 -1
- package/src/run-watch.ts +1 -1
- package/src/run-wayfind-outline.ts +6 -2
- package/src/trails/adapter-check.ts +8 -8
- package/src/trails/add-surface.ts +2 -2
- package/src/trails/add-trail.ts +3 -3
- package/src/trails/add-verify.ts +2 -2
- package/src/trails/compile.ts +9 -9
- package/src/trails/completions-complete.ts +10 -10
- package/src/trails/completions.ts +2 -2
- package/src/trails/create-adapter.ts +185 -424
- package/src/trails/create-scaffold.ts +12 -12
- package/src/trails/create-versions.ts +8 -8
- package/src/trails/create.ts +35 -35
- package/src/trails/deprecate.ts +2 -2
- package/src/trails/dev-clean.ts +11 -11
- package/src/trails/dev-reset.ts +11 -11
- package/src/trails/dev-stats.ts +8 -8
- package/src/trails/dev-support.ts +1 -1
- package/src/trails/doctor.ts +4 -4
- package/src/trails/draft-promote.ts +3 -3
- package/src/trails/guide.ts +9 -9
- package/src/trails/load-app.ts +3 -3
- package/src/trails/regrade.ts +56 -27
- package/src/trails/release-check.ts +8 -8
- package/src/trails/release-smoke.ts +2 -2
- package/src/trails/revise.ts +2 -2
- package/src/trails/run-example.ts +9 -9
- package/src/trails/run-examples.ts +9 -9
- package/src/trails/run.ts +26 -26
- package/src/trails/survey.ts +45 -45
- package/src/trails/topo-activation.ts +2 -2
- package/src/trails/topo-history.ts +8 -8
- package/src/trails/topo-output-schemas.ts +5 -5
- package/src/trails/topo-pin.ts +6 -6
- package/src/trails/topo-read-support.ts +4 -3
- package/src/trails/topo-reports.ts +16 -16
- package/src/trails/topo-store-support.ts +18 -9
- package/src/trails/topo-support.ts +2 -2
- package/src/trails/topo-unpin.ts +12 -12
- package/src/trails/topo.ts +4 -4
- package/src/trails/validate.ts +2 -2
- package/src/trails/version-lifecycle-support.ts +17 -16
- package/src/trails/warden-guide.ts +8 -8
- package/src/trails/warden.ts +21 -21
- package/src/trails/wayfind-outline.ts +876 -0
- package/src/trails/wayfind.ts +74 -74
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,93 @@
|
|
|
1
1
|
# trails
|
|
2
2
|
|
|
3
|
+
## 1.0.0-beta.41
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`19406e0`](https://github.com/outfitter-dev/trails/commit/19406e06f825c5ba4460b153eaf2ade721a749d7): Classify unauthenticated scoped-package dist-tag fallbacks as first publications after npm has already reported the package missing, and preserve registry probe details in release-policy blockers.
|
|
8
|
+
|
|
9
|
+
## 1.0.0-beta.40
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- [`5adb995`](https://github.com/outfitter-dev/trails/commit/5adb99551c2dda6190d46cce7f60bb08d63c99aa): Complete the v1 hard cutover from the authored `blaze` field to
|
|
14
|
+
`implementation` across trail contracts, surface projections, tests, examples,
|
|
15
|
+
and public source-analysis helpers. Existing applications must rename authored
|
|
16
|
+
trail behavior fields and direct trail-object access before upgrading.
|
|
17
|
+
- [`6712075`](https://github.com/outfitter-dev/trails/commit/67120754df3f614c7f4dd98be1fa0ba9d69b7765): Complete the v1 hard cutover from the `contour` domain-object declaration
|
|
18
|
+
vocabulary to `entity` across contracts, topo facts, store helpers, Warden,
|
|
19
|
+
Wayfinder, operator surfaces, examples, and generated locks. Existing
|
|
20
|
+
applications must rename contour APIs, run `trails dev reset --yes` to discard
|
|
21
|
+
pre-cutover local Topographer snapshots, and then recompile committed
|
|
22
|
+
`trails.lock` artifacts before upgrading. Those derived snapshots are
|
|
23
|
+
intentionally not read through a compatibility layer.
|
|
24
|
+
The entity-shaped wire contract advances `TopoGraph` and split lock manifests
|
|
25
|
+
from schema version 3 to 4; old split artifacts fail with regeneration guidance,
|
|
26
|
+
while the canonical root `trails.lock` remains schema version 5.
|
|
27
|
+
Wayfinder reports those stale rows as topo-store drift while keeping current
|
|
28
|
+
committed lock facts available for inspection.
|
|
29
|
+
|
|
30
|
+
### Patch Changes
|
|
31
|
+
|
|
32
|
+
- [`4def007`](https://github.com/outfitter-dev/trails/commit/4def007a53c42881fba1d958a67f0c05f417e811): Move adapter source export scanning into adapter-kit and have `create.adapter`
|
|
33
|
+
consume the shared helper.
|
|
34
|
+
- [`78575d5`](https://github.com/outfitter-dev/trails/commit/78575d5193242053b4dc1f4fa6150b94bacaff44): Discover owner-package subpath adapter subjects in shared adapter checks and
|
|
35
|
+
enable `trails create adapter --placement subpath` to generate immediately
|
|
36
|
+
checkable owner subpaths.
|
|
37
|
+
- [`35cbe28`](https://github.com/outfitter-dev/trails/commit/35cbe289db46539b3689dbf6cf8ab0e5d9a1b09c): Found `@ontrails/source` as the shared source-code AST kernel for parsing,
|
|
38
|
+
walking, locations, edits, literals, and generic Trails syntax recognition.
|
|
39
|
+
Warden, Regrade, Wayfinder, and the Trails operator now import those shared
|
|
40
|
+
mechanics from `@ontrails/source`; the legacy Warden AST route is removed by the
|
|
41
|
+
stacked hard cutover.
|
|
42
|
+
- [`10f2492`](https://github.com/outfitter-dev/trails/commit/10f24928d3bc9d995abf7aa261ecf515c295855d): Own the `wayfind.outline` implementation in the Trails operator app while preserving the existing `trails wayfind file <file> --outline` CLI and MCP composition behavior, and document `@ontrails/source` as the operator's live-source analysis kernel.
|
|
43
|
+
- [`35e5fed`](https://github.com/outfitter-dev/trails/commit/35e5fedd228e498783f479f0dd502e2f3ec772b8): Fold the Wayfinder graph-read catalog into `@ontrails/topography`. Wayfind
|
|
44
|
+
remains the product, trail-id, CLI, and MCP brand, but there is no longer an
|
|
45
|
+
`@ontrails/wayfinder` package to install or import. Programmatic consumers
|
|
46
|
+
should move imports such as `wayfinderTopo`, `wayfindOverviewTrail`,
|
|
47
|
+
`loadWayfinderArtifacts`, and the Wayfinder filter/provenance types to
|
|
48
|
+
`@ontrails/topography`.
|
|
49
|
+
|
|
50
|
+
Expose that package move as a governed Regrade transition so exact
|
|
51
|
+
`@ontrails/wayfinder` imports can move safely while product vocabulary and near
|
|
52
|
+
routes remain unchanged for review. Regrade routes package manifests through
|
|
53
|
+
structured review instead of rewriting dependency keys as plain text.
|
|
54
|
+
|
|
55
|
+
The Trails operator now reads all `wayfind.*` query trails and artifact helpers
|
|
56
|
+
from `@ontrails/topography` while preserving the existing CLI/MCP schemas,
|
|
57
|
+
route IDs, output shapes, and internal trail visibility.
|
|
58
|
+
|
|
59
|
+
- [`3a65ae3`](https://github.com/outfitter-dev/trails/commit/3a65ae363e05b7589f4a9876da4346886353b48c): Rename the durable graph substrate package from `@ontrails/topographer` to
|
|
60
|
+
`@ontrails/topography` after folding Wayfind graph queries into that owner.
|
|
61
|
+
|
|
62
|
+
Update imports to `@ontrails/topography` or
|
|
63
|
+
`@ontrails/topography/backend-support`. The pre-1.0 cutover does not ship a
|
|
64
|
+
compatibility package. TopoGraph, lock, topo-store, semantic diff, and Wayfind
|
|
65
|
+
APIs keep their existing contracts, and the `trails wayfind` CLI and MCP names
|
|
66
|
+
remain unchanged.
|
|
67
|
+
|
|
68
|
+
The governed package-route transition moves legacy `@ontrails/wayfinder`
|
|
69
|
+
imports directly to `@ontrails/topography`; it does not emit the retired
|
|
70
|
+
intermediate `@ontrails/topographer` route.
|
|
71
|
+
|
|
72
|
+
- [`e5edace`](https://github.com/outfitter-dev/trails/commit/e5edace60281c76157512acc6e1446c8415633ba): Make Regrade history hashes stable across report serialization and preserve
|
|
73
|
+
pre-apply occurrence evidence alongside truthful completion counts and a
|
|
74
|
+
freshly scanned post-apply completion report for replay detection. Existing
|
|
75
|
+
plans and history stamped with the earlier hash serializer remain valid.
|
|
76
|
+
- [`4030698`](https://github.com/outfitter-dev/trails/commit/40306984467625844564f0f84156530d7118a79c): Keep structured input on nested child commands from being reinterpreted as a
|
|
77
|
+
bare child-name positional fallback, while preserving schema-authored
|
|
78
|
+
`inputJson` flags as ordinary trail input, including through the public Trails
|
|
79
|
+
CLI. Optional numeric flags now consume negative values with Commander's own
|
|
80
|
+
parsing semantics, and variadic flags consume every following value, before
|
|
81
|
+
nested command routing is resolved.
|
|
82
|
+
- [`8a1ac00`](https://github.com/outfitter-dev/trails/commit/8a1ac00b5d789be41ca6e464358c96b01e442bf4): Govern the exact `@ontrails/warden/ast` to `@ontrails/source` package route
|
|
83
|
+
transition for Regrade string-literal and module-specifier rewrites exposed
|
|
84
|
+
through the Trails CLI and MCP tools. Safe rewrites now require the owning
|
|
85
|
+
manifest to already declare the target package; otherwise Regrade preserves the
|
|
86
|
+
occurrence with dependency repair guidance. Invalid manifests remain unchanged
|
|
87
|
+
and produce structured repair guidance that names the owning manifest. Explicit
|
|
88
|
+
preserve rules remain no-ops before dependency validation, and dotted or
|
|
89
|
+
subpath-like near routes remain deferred instead of becoming invented imports.
|
|
90
|
+
|
|
3
91
|
## 1.0.0-beta.39
|
|
4
92
|
|
|
5
93
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ Common workflows:
|
|
|
15
15
|
- `trails topo` inspects topo state and manages pins/history.
|
|
16
16
|
- `trails compile` writes root `trails.lock`.
|
|
17
17
|
- `trails validate` checks root `trails.lock` for drift.
|
|
18
|
-
- `trails wayfind`, `trails wayfind --trails --intent read`, `trails wayfind <id> --contract`, `trails wayfind <id> --deps`, `trails wayfind <id> --impact`, `trails wayfind pattern "wayfind.*"`, `trails wayfind query "release drift"`, `trails wayfind file <file> --outline
|
|
18
|
+
- `trails wayfind`, `trails wayfind --trails --intent read`, `trails wayfind <id> --contract`, `trails wayfind <id> --deps`, `trails wayfind <id> --impact`, `trails wayfind pattern "wayfind.*"`, `trails wayfind query "release drift"`, and `trails wayfind diff ...` read graph artifacts through Wayfinder. `trails wayfind file <file> --outline` is the operator-owned live-source exception, assembled with `@ontrails/source` and enriched with saved graph context when available.
|
|
19
19
|
- `trails schema <command...>` shows accepted CLI routes, aliases, flags, and schemas for an operator command or command namespace.
|
|
20
20
|
- `trails warden` runs Trails governance checks for contract and architecture drift. Use `--scope-exclude <glob>` or project `warden.scope.exclude` config when local notes, scratch space, or generated state should not be governed by Warden.
|
|
21
21
|
- `trails regrade plan <from> <to> --root-dir <path> --json` writes an active Regrade plan, `trails regrade plan --expand` stages wide-net review candidates in that plan, `trails regrade preview` reruns it without writing, and `trails regrade apply` consumes the plan and writes history. Use `plan --expand --dry-run` to inspect staged candidates without writing the active plan, and add `--dry-run` to `apply` to prove the apply path without mutating source. Use `trails regrade plans` and `trails regrade check` when a workspace has active plans that need inspection before apply. MCP exposes the same lifecycle as `trails_plan_regrade`, `trails_list_regrades`, `trails_check_regrade`, `trails_preview_regrade`, and `trails_apply_regrade`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ontrails/trails",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.41",
|
|
4
4
|
"bin": {
|
|
5
5
|
"trails": "./bin/trails.ts"
|
|
6
6
|
},
|
|
@@ -27,26 +27,26 @@
|
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@clack/prompts": "^1.1.0",
|
|
30
|
-
"@ontrails/adapter-kit": "^1.0.0-beta.
|
|
31
|
-
"@ontrails/cli": "^1.0.0-beta.
|
|
32
|
-
"@ontrails/commander": "^1.0.0-beta.
|
|
33
|
-
"@ontrails/config": "^1.0.0-beta.
|
|
34
|
-
"@ontrails/core": "^1.0.0-beta.
|
|
35
|
-
"@ontrails/http": "^1.0.0-beta.
|
|
36
|
-
"@ontrails/mcp": "^1.0.0-beta.
|
|
37
|
-
"@ontrails/observe": "^1.0.0-beta.
|
|
38
|
-
"@ontrails/permits": "^1.0.0-beta.
|
|
39
|
-
"@ontrails/regrade": "^1.0.0-beta.
|
|
40
|
-
"@ontrails/
|
|
41
|
-
"@ontrails/
|
|
42
|
-
"@ontrails/
|
|
43
|
-
"@ontrails/
|
|
30
|
+
"@ontrails/adapter-kit": "^1.0.0-beta.41",
|
|
31
|
+
"@ontrails/cli": "^1.0.0-beta.41",
|
|
32
|
+
"@ontrails/commander": "^1.0.0-beta.41",
|
|
33
|
+
"@ontrails/config": "^1.0.0-beta.41",
|
|
34
|
+
"@ontrails/core": "^1.0.0-beta.41",
|
|
35
|
+
"@ontrails/http": "^1.0.0-beta.41",
|
|
36
|
+
"@ontrails/mcp": "^1.0.0-beta.41",
|
|
37
|
+
"@ontrails/observe": "^1.0.0-beta.41",
|
|
38
|
+
"@ontrails/permits": "^1.0.0-beta.41",
|
|
39
|
+
"@ontrails/regrade": "^1.0.0-beta.41",
|
|
40
|
+
"@ontrails/source": "^1.0.0-beta.41",
|
|
41
|
+
"@ontrails/topography": "^1.0.0-beta.41",
|
|
42
|
+
"@ontrails/tracing": "^1.0.0-beta.41",
|
|
43
|
+
"@ontrails/warden": "^1.0.0-beta.41",
|
|
44
44
|
"commander": "^14.0.3",
|
|
45
45
|
"typescript": "^5.9.3",
|
|
46
46
|
"zod": "^4.3.5"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@ontrails/cloudflare": "^1.0.0-beta.
|
|
50
|
-
"@ontrails/testing": "^1.0.0-beta.
|
|
49
|
+
"@ontrails/cloudflare": "^1.0.0-beta.41",
|
|
50
|
+
"@ontrails/testing": "^1.0.0-beta.41"
|
|
51
51
|
}
|
|
52
52
|
}
|
package/src/app.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { surfaceOverlay, topo } from '@ontrails/core';
|
|
2
2
|
import {
|
|
3
3
|
wayfindAdaptersTrail,
|
|
4
|
-
|
|
4
|
+
wayfindEntitiesTrail,
|
|
5
5
|
wayfindContractTrail,
|
|
6
6
|
wayfindDescribeTrail,
|
|
7
7
|
wayfindDiffTrail,
|
|
@@ -12,14 +12,13 @@ import {
|
|
|
12
12
|
wayfindImpactTrail,
|
|
13
13
|
wayfindNearbyTrail,
|
|
14
14
|
wayfindOverviewTrail,
|
|
15
|
-
wayfindOutlineTrail,
|
|
16
15
|
wayfindResourcesTrail,
|
|
17
16
|
wayfindSearchTrail,
|
|
18
17
|
wayfindSignalsTrail,
|
|
19
18
|
wayfindSurfacesTrail,
|
|
20
19
|
wayfindTrailsTrail,
|
|
21
20
|
wayfindVersionsTrail,
|
|
22
|
-
} from '@ontrails/
|
|
21
|
+
} from '@ontrails/topography';
|
|
23
22
|
|
|
24
23
|
import * as addSurface from './trails/add-surface.js';
|
|
25
24
|
import * as addTrail from './trails/add-trail.js';
|
|
@@ -55,6 +54,7 @@ import * as validate from './trails/validate.js';
|
|
|
55
54
|
import * as warden from './trails/warden.js';
|
|
56
55
|
import * as wardenGuide from './trails/warden-guide.js';
|
|
57
56
|
import * as wayfind from './trails/wayfind.js';
|
|
57
|
+
import { wayfindOutlineTrail } from './trails/wayfind-outline.js';
|
|
58
58
|
|
|
59
59
|
export const operatorApp = topo(
|
|
60
60
|
'trails',
|
|
@@ -107,9 +107,9 @@ const cliWayfinderCommandTrails = {
|
|
|
107
107
|
|
|
108
108
|
const cliWayfinderSupportTrails = {
|
|
109
109
|
wayfindAdaptersTrail,
|
|
110
|
-
wayfindContoursTrail,
|
|
111
110
|
wayfindContractTrail,
|
|
112
111
|
wayfindDescribeTrail,
|
|
112
|
+
wayfindEntitiesTrail,
|
|
113
113
|
wayfindErrorsTrail,
|
|
114
114
|
wayfindExamplesTrail,
|
|
115
115
|
wayfindImpactTrail,
|
package/src/cli.ts
CHANGED
|
@@ -19,7 +19,7 @@ import type {
|
|
|
19
19
|
import { createProgram } from '@ontrails/commander';
|
|
20
20
|
import type { CreateProgramOptions } from '@ontrails/commander';
|
|
21
21
|
import { resolvePermitFromBearerToken } from '@ontrails/permits';
|
|
22
|
-
import { deriveTopoGraph } from '@ontrails/
|
|
22
|
+
import { deriveTopoGraph } from '@ontrails/topography';
|
|
23
23
|
|
|
24
24
|
import { app, trailsCliIncludedTrails, trailsOverlays } from './app.js';
|
|
25
25
|
import { resolveInputWithClack } from './clack.js';
|
package/src/completions.ts
CHANGED
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
Result,
|
|
22
22
|
ValidationError,
|
|
23
23
|
} from '@ontrails/core';
|
|
24
|
-
import { buildWorkspaceTrailIndex } from '@ontrails/
|
|
24
|
+
import { buildWorkspaceTrailIndex } from '@ontrails/topography';
|
|
25
25
|
|
|
26
26
|
import { tryLoadFreshAppLease } from './trails/load-app.js';
|
|
27
27
|
|
package/src/mcp-app.ts
CHANGED
|
@@ -10,14 +10,14 @@ import {
|
|
|
10
10
|
wayfindImpactTrail,
|
|
11
11
|
wayfindNearbyTrail,
|
|
12
12
|
wayfindOverviewTrail,
|
|
13
|
-
wayfindOutlineTrail,
|
|
14
13
|
wayfindResourcesTrail,
|
|
15
14
|
wayfindSearchTrail,
|
|
16
15
|
wayfindSurfacesTrail,
|
|
17
16
|
wayfindTrailsTrail,
|
|
18
|
-
} from '@ontrails/
|
|
17
|
+
} from '@ontrails/topography';
|
|
19
18
|
|
|
20
19
|
import { operatorApp } from './app.js';
|
|
20
|
+
import { wayfindOutlineTrail } from './trails/wayfind-outline.js';
|
|
21
21
|
|
|
22
22
|
const operatorTrails = Object.fromEntries(
|
|
23
23
|
operatorApp.list().map((trailItem) => [trailItem.id, trailItem])
|
package/src/regrade/history.ts
CHANGED
|
@@ -19,7 +19,10 @@ import {
|
|
|
19
19
|
regradePlanContentHash,
|
|
20
20
|
regradePlanDirectory,
|
|
21
21
|
regradePlanSlugForBody,
|
|
22
|
+
legacyRegradeSourceHash,
|
|
22
23
|
regradeSourceHash,
|
|
24
|
+
regradeSourceHashMatches,
|
|
25
|
+
regradeSourceHashes,
|
|
23
26
|
rootRelativePath,
|
|
24
27
|
} from './plan-artifact.js';
|
|
25
28
|
import type { RegradePlanArtifact, RegradePlanBody } from './plan-artifact.js';
|
|
@@ -30,8 +33,23 @@ import type { RegradePlanArtifact, RegradePlanBody } from './plan-artifact.js';
|
|
|
30
33
|
*/
|
|
31
34
|
export const REGRADE_HISTORY_SCHEMA_VERSION = 2;
|
|
32
35
|
|
|
36
|
+
const rawCompletionReportHash = Symbol('rawCompletionReportHash');
|
|
37
|
+
const rawReportHash = Symbol('rawReportHash');
|
|
38
|
+
const rawCompletionReport = Symbol('rawCompletionReport');
|
|
39
|
+
const rawReport = Symbol('rawReport');
|
|
40
|
+
|
|
33
41
|
const regradeHistoryRunSchema = z
|
|
34
42
|
.object({
|
|
43
|
+
completionReport: regradeReportOutput
|
|
44
|
+
.optional()
|
|
45
|
+
.describe(
|
|
46
|
+
'Post-apply source evidence used to recognize a completed-state replay'
|
|
47
|
+
),
|
|
48
|
+
completionReportHash: z
|
|
49
|
+
.string()
|
|
50
|
+
.regex(/^[0-9a-f]{64}$/)
|
|
51
|
+
.optional()
|
|
52
|
+
.describe('Canonical hash of the post-apply completion report'),
|
|
35
53
|
lockHashAtRun: z
|
|
36
54
|
.string()
|
|
37
55
|
.min(1)
|
|
@@ -58,6 +76,12 @@ const regradeHistoryArtifactSchema = z
|
|
|
58
76
|
.strict();
|
|
59
77
|
|
|
60
78
|
interface RegradeHistoryRun {
|
|
79
|
+
readonly [rawCompletionReportHash]?: string;
|
|
80
|
+
readonly [rawCompletionReport]?: RegradeReport;
|
|
81
|
+
readonly [rawReportHash]?: string;
|
|
82
|
+
readonly [rawReport]?: RegradeReport;
|
|
83
|
+
readonly completionReport: RegradeReport;
|
|
84
|
+
readonly completionReportHash: string;
|
|
61
85
|
readonly lockHashAtRun: string;
|
|
62
86
|
readonly plan: RegradePlanArtifact;
|
|
63
87
|
readonly planContentHash: string;
|
|
@@ -115,7 +139,46 @@ export const readRegradeHistoryArtifact = (
|
|
|
115
139
|
})
|
|
116
140
|
);
|
|
117
141
|
}
|
|
118
|
-
|
|
142
|
+
const rawRuns = (
|
|
143
|
+
parsedJson as {
|
|
144
|
+
readonly runs: readonly {
|
|
145
|
+
readonly completionReport?: RegradeReport;
|
|
146
|
+
readonly report: RegradeReport;
|
|
147
|
+
}[];
|
|
148
|
+
}
|
|
149
|
+
).runs;
|
|
150
|
+
return Result.ok({
|
|
151
|
+
...parsed.data,
|
|
152
|
+
runs: parsed.data.runs.map((run, index) => {
|
|
153
|
+
// Early schema-v2 histories recorded only the post-apply report. Treat
|
|
154
|
+
// that report and its lock hash as completion evidence when reading
|
|
155
|
+
// those artifacts.
|
|
156
|
+
const completionReport = run.completionReport ?? run.report;
|
|
157
|
+
const normalized = {
|
|
158
|
+
...run,
|
|
159
|
+
completionReport,
|
|
160
|
+
completionReportHash: run.completionReportHash ?? run.lockHashAtRun,
|
|
161
|
+
} as RegradeHistoryRun;
|
|
162
|
+
const rawRun = rawRuns[index];
|
|
163
|
+
if (rawRun !== undefined) {
|
|
164
|
+
Object.defineProperties(normalized, {
|
|
165
|
+
[rawCompletionReport]: {
|
|
166
|
+
value: rawRun.completionReport ?? rawRun.report,
|
|
167
|
+
},
|
|
168
|
+
[rawCompletionReportHash]: {
|
|
169
|
+
value: legacyRegradeSourceHash(
|
|
170
|
+
rawRun.completionReport ?? rawRun.report
|
|
171
|
+
),
|
|
172
|
+
},
|
|
173
|
+
[rawReportHash]: {
|
|
174
|
+
value: legacyRegradeSourceHash(rawRun.report),
|
|
175
|
+
},
|
|
176
|
+
[rawReport]: { value: rawRun.report },
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
return normalized;
|
|
180
|
+
}),
|
|
181
|
+
} as RegradeHistoryArtifact);
|
|
119
182
|
};
|
|
120
183
|
|
|
121
184
|
/**
|
|
@@ -132,14 +195,66 @@ export const mintTransitionId = (
|
|
|
132
195
|
.digest('hex')
|
|
133
196
|
.slice(0, 12);
|
|
134
197
|
|
|
198
|
+
/**
|
|
199
|
+
* Verify every recorded run at its own stamped lock: recompute the plan
|
|
200
|
+
* content hash and lock hash from the recorded plan and report, then compare
|
|
201
|
+
* with the stamped values.
|
|
202
|
+
*/
|
|
203
|
+
export const verifyRegradeHistoryRuns = (
|
|
204
|
+
artifact: RegradeHistoryArtifact
|
|
205
|
+
): TrailsResult<{ readonly runs: number }, ValidationError> => {
|
|
206
|
+
for (const [index, run] of artifact.runs.entries()) {
|
|
207
|
+
if (regradePlanContentHash(run.plan.plan) !== run.planContentHash) {
|
|
208
|
+
return Result.err(
|
|
209
|
+
new ValidationError('Regrade history run stamp mismatch.', {
|
|
210
|
+
context: {
|
|
211
|
+
field: 'planContentHash',
|
|
212
|
+
path: artifact.path,
|
|
213
|
+
run: index,
|
|
214
|
+
},
|
|
215
|
+
})
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
if (
|
|
219
|
+
!regradeSourceHashMatches(run.lockHashAtRun, run.report) &&
|
|
220
|
+
run[rawReportHash] !== run.lockHashAtRun
|
|
221
|
+
) {
|
|
222
|
+
return Result.err(
|
|
223
|
+
new ValidationError('Regrade history run stamp mismatch.', {
|
|
224
|
+
context: { field: 'lockHashAtRun', path: artifact.path, run: index },
|
|
225
|
+
})
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
if (
|
|
229
|
+
!regradeSourceHashMatches(
|
|
230
|
+
run.completionReportHash,
|
|
231
|
+
run.completionReport
|
|
232
|
+
) &&
|
|
233
|
+
run[rawCompletionReportHash] !== run.completionReportHash
|
|
234
|
+
) {
|
|
235
|
+
return Result.err(
|
|
236
|
+
new ValidationError('Regrade history run stamp mismatch.', {
|
|
237
|
+
context: {
|
|
238
|
+
field: 'completionReportHash',
|
|
239
|
+
path: artifact.path,
|
|
240
|
+
run: index,
|
|
241
|
+
},
|
|
242
|
+
})
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return Result.ok({ runs: artifact.runs.length });
|
|
247
|
+
};
|
|
248
|
+
|
|
135
249
|
/**
|
|
136
250
|
* Append one applied run to the transition's consolidated history file. A
|
|
137
|
-
* run whose plan content hash and
|
|
138
|
-
*
|
|
139
|
-
* surfaced instead of a duplicate record.
|
|
251
|
+
* run whose plan content hash and source evidence equal either the last run's
|
|
252
|
+
* pre-apply report or completed state is a replay: nothing is written and
|
|
253
|
+
* `status: 'replay'` is surfaced instead of a duplicate record.
|
|
140
254
|
*/
|
|
141
255
|
export const appendRegradeHistoryRun = (params: {
|
|
142
256
|
readonly artifact: RegradePlanArtifact;
|
|
257
|
+
readonly completedReport?: RegradeReport;
|
|
143
258
|
readonly report: RegradeReport;
|
|
144
259
|
readonly rootDir: string;
|
|
145
260
|
}): TrailsResult<RegradeHistorySummary, Error> => {
|
|
@@ -150,7 +265,11 @@ export const appendRegradeHistoryRun = (params: {
|
|
|
150
265
|
const relativePath = rootRelativePath(params.rootDir, absolutePath);
|
|
151
266
|
const planContentHash = regradePlanContentHash(params.artifact.plan);
|
|
152
267
|
const lockHashAtRun = regradeSourceHash(params.report);
|
|
268
|
+
const currentSourceHashes = regradeSourceHashes(params.report);
|
|
269
|
+
const completionReport = params.completedReport ?? params.report;
|
|
153
270
|
const entry: RegradeHistoryRun = {
|
|
271
|
+
completionReport,
|
|
272
|
+
completionReportHash: regradeSourceHash(completionReport),
|
|
154
273
|
lockHashAtRun,
|
|
155
274
|
plan: params.artifact,
|
|
156
275
|
planContentHash,
|
|
@@ -164,6 +283,10 @@ export const appendRegradeHistoryRun = (params: {
|
|
|
164
283
|
return existing;
|
|
165
284
|
}
|
|
166
285
|
prior = existing.value;
|
|
286
|
+
const verified = verifyRegradeHistoryRuns(prior);
|
|
287
|
+
if (verified.isErr()) {
|
|
288
|
+
return verified;
|
|
289
|
+
}
|
|
167
290
|
if (
|
|
168
291
|
params.artifact.transitionId !== undefined &&
|
|
169
292
|
params.artifact.transitionId !== prior.id
|
|
@@ -208,7 +331,8 @@ export const appendRegradeHistoryRun = (params: {
|
|
|
208
331
|
if (
|
|
209
332
|
lastRun !== undefined &&
|
|
210
333
|
lastRun.planContentHash === planContentHash &&
|
|
211
|
-
lastRun.lockHashAtRun
|
|
334
|
+
(currentSourceHashes.includes(lastRun.lockHashAtRun) ||
|
|
335
|
+
currentSourceHashes.includes(lastRun.completionReportHash))
|
|
212
336
|
) {
|
|
213
337
|
return Result.ok({
|
|
214
338
|
path: relativePath,
|
|
@@ -233,7 +357,21 @@ export const appendRegradeHistoryRun = (params: {
|
|
|
233
357
|
runs: prior === undefined ? [entry] : [...prior.runs, entry],
|
|
234
358
|
schemaVersion: REGRADE_HISTORY_SCHEMA_VERSION,
|
|
235
359
|
};
|
|
236
|
-
const
|
|
360
|
+
const writableArtifact = {
|
|
361
|
+
id: artifact.id,
|
|
362
|
+
kind: artifact.kind,
|
|
363
|
+
path: artifact.path,
|
|
364
|
+
runs: artifact.runs.map((run) => ({
|
|
365
|
+
completionReport: run[rawCompletionReport] ?? run.completionReport,
|
|
366
|
+
completionReportHash: run.completionReportHash,
|
|
367
|
+
lockHashAtRun: run.lockHashAtRun,
|
|
368
|
+
plan: run.plan,
|
|
369
|
+
planContentHash: run.planContentHash,
|
|
370
|
+
report: run[rawReport] ?? run.report,
|
|
371
|
+
})),
|
|
372
|
+
schemaVersion: artifact.schemaVersion,
|
|
373
|
+
};
|
|
374
|
+
const parsed = regradeHistoryArtifactSchema.safeParse(writableArtifact);
|
|
237
375
|
if (!parsed.success) {
|
|
238
376
|
return Result.err(
|
|
239
377
|
new ValidationError('Invalid Regrade history artifact.', {
|
|
@@ -243,7 +381,10 @@ export const appendRegradeHistoryRun = (params: {
|
|
|
243
381
|
}
|
|
244
382
|
try {
|
|
245
383
|
mkdirSync(dirname(absolutePath), { recursive: true });
|
|
246
|
-
writeFileSync(
|
|
384
|
+
writeFileSync(
|
|
385
|
+
absolutePath,
|
|
386
|
+
`${JSON.stringify(writableArtifact, null, 2)}\n`
|
|
387
|
+
);
|
|
247
388
|
} catch (error) {
|
|
248
389
|
return Result.err(
|
|
249
390
|
new InternalError('Failed to write Regrade history entry.', {
|
|
@@ -259,37 +400,6 @@ export const appendRegradeHistoryRun = (params: {
|
|
|
259
400
|
});
|
|
260
401
|
};
|
|
261
402
|
|
|
262
|
-
/**
|
|
263
|
-
* Verify every recorded run at its own stamped lock: recompute the plan
|
|
264
|
-
* content hash and lock hash from the recorded plan and report, then compare
|
|
265
|
-
* with the stamped values.
|
|
266
|
-
*/
|
|
267
|
-
export const verifyRegradeHistoryRuns = (
|
|
268
|
-
artifact: RegradeHistoryArtifact
|
|
269
|
-
): TrailsResult<{ readonly runs: number }, ValidationError> => {
|
|
270
|
-
for (const [index, run] of artifact.runs.entries()) {
|
|
271
|
-
if (regradePlanContentHash(run.plan.plan) !== run.planContentHash) {
|
|
272
|
-
return Result.err(
|
|
273
|
-
new ValidationError('Regrade history run stamp mismatch.', {
|
|
274
|
-
context: {
|
|
275
|
-
field: 'planContentHash',
|
|
276
|
-
path: artifact.path,
|
|
277
|
-
run: index,
|
|
278
|
-
},
|
|
279
|
-
})
|
|
280
|
-
);
|
|
281
|
-
}
|
|
282
|
-
if (regradeSourceHash(run.report) !== run.lockHashAtRun) {
|
|
283
|
-
return Result.err(
|
|
284
|
-
new ValidationError('Regrade history run stamp mismatch.', {
|
|
285
|
-
context: { field: 'lockHashAtRun', path: artifact.path, run: index },
|
|
286
|
-
})
|
|
287
|
-
);
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
return Result.ok({ runs: artifact.runs.length });
|
|
291
|
-
};
|
|
292
|
-
|
|
293
403
|
const hasPathSeparator = (value: string): boolean =>
|
|
294
404
|
value.includes('/') || value.includes('\\');
|
|
295
405
|
|
|
@@ -223,17 +223,6 @@ const sourceHashEntryFacts = (
|
|
|
223
223
|
...(reviewDetails === undefined ? {} : { reviewDetails }),
|
|
224
224
|
}));
|
|
225
225
|
|
|
226
|
-
export const regradeSourceHash = (report: RegradeReport): string =>
|
|
227
|
-
createHash('sha256')
|
|
228
|
-
.update(
|
|
229
|
-
JSON.stringify({
|
|
230
|
-
entries: sourceHashEntryFacts(report.entries),
|
|
231
|
-
ledger: report.run?.ledger,
|
|
232
|
-
selectedClassIds: report.selectedClassIds,
|
|
233
|
-
})
|
|
234
|
-
)
|
|
235
|
-
.digest('hex');
|
|
236
|
-
|
|
237
226
|
const canonicalizeJsonValue = (value: unknown): unknown => {
|
|
238
227
|
if (Array.isArray(value)) {
|
|
239
228
|
return value.map((entry) => canonicalizeJsonValue(entry));
|
|
@@ -256,6 +245,37 @@ const canonicalizeJsonValue = (value: unknown): unknown => {
|
|
|
256
245
|
export const canonicalJsonStringify = (value: unknown): string =>
|
|
257
246
|
JSON.stringify(canonicalizeJsonValue(value));
|
|
258
247
|
|
|
248
|
+
const regradeSourceHashFacts = (report: RegradeReport): unknown => ({
|
|
249
|
+
entries: sourceHashEntryFacts(report.entries),
|
|
250
|
+
ledger: report.run?.ledger,
|
|
251
|
+
selectedClassIds: report.selectedClassIds,
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
const hashSerializedSourceFacts = (serialized: string): string =>
|
|
255
|
+
createHash('sha256').update(serialized).digest('hex');
|
|
256
|
+
|
|
257
|
+
export const regradeSourceHash = (report: RegradeReport): string =>
|
|
258
|
+
hashSerializedSourceFacts(
|
|
259
|
+
canonicalJsonStringify(regradeSourceHashFacts(report))
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
export const legacyRegradeSourceHash = (report: RegradeReport): string =>
|
|
263
|
+
hashSerializedSourceFacts(JSON.stringify(regradeSourceHashFacts(report)));
|
|
264
|
+
|
|
265
|
+
/** Match source evidence written before canonical JSON hashing. */
|
|
266
|
+
export const regradeSourceHashMatches = (
|
|
267
|
+
stampedHash: string,
|
|
268
|
+
report: RegradeReport
|
|
269
|
+
): boolean =>
|
|
270
|
+
stampedHash === regradeSourceHash(report) ||
|
|
271
|
+
stampedHash === legacyRegradeSourceHash(report);
|
|
272
|
+
|
|
273
|
+
export const regradeSourceHashes = (
|
|
274
|
+
report: RegradeReport
|
|
275
|
+
): readonly string[] => [
|
|
276
|
+
...new Set([regradeSourceHash(report), legacyRegradeSourceHash(report)]),
|
|
277
|
+
];
|
|
278
|
+
|
|
259
279
|
/**
|
|
260
280
|
* Canonical content hash of a resolved Regrade plan body — the authored
|
|
261
281
|
* migration intent. Stable across key insertion order; changes on any edit to
|
|
@@ -339,6 +339,11 @@ const isNpmNotFoundOutput = (stdout: string, stderr: string): boolean => {
|
|
|
339
339
|
return combined.includes('E404') || combined.includes('404 Not Found');
|
|
340
340
|
};
|
|
341
341
|
|
|
342
|
+
const isNpmUnauthorizedOutput = (stdout: string, stderr: string): boolean => {
|
|
343
|
+
const combined = `${stdout}\n${stderr}`;
|
|
344
|
+
return combined.includes('E401') || combined.includes('401 Unauthorized');
|
|
345
|
+
};
|
|
346
|
+
|
|
342
347
|
const isNpmExactVersionMissingOutput = (
|
|
343
348
|
stdout: string,
|
|
344
349
|
stderr: string
|
|
@@ -403,7 +408,13 @@ const npmDistTagRegistryView = async (
|
|
|
403
408
|
): Promise<NpmView | null> => {
|
|
404
409
|
const { exitCode, stderr, stdout } = await runNpm(['dist-tag', 'ls', name]);
|
|
405
410
|
if (exitCode !== 0) {
|
|
406
|
-
|
|
411
|
+
// npm returns E401 for the dist-tag endpoint of an unpublished scoped
|
|
412
|
+
// package, even though the preceding package view returned E404. At this
|
|
413
|
+
// fallback boundary both responses mean the package does not exist yet.
|
|
414
|
+
if (
|
|
415
|
+
isNpmNotFoundOutput(stdout, stderr) ||
|
|
416
|
+
isNpmUnauthorizedOutput(stdout, stderr)
|
|
417
|
+
) {
|
|
407
418
|
return null;
|
|
408
419
|
}
|
|
409
420
|
throw new Error(stderr.trim() || `npm dist-tag ls failed for ${name}`);
|
package/src/release/policy.ts
CHANGED
|
@@ -59,6 +59,7 @@ export interface ReleasePolicySourcePullRequest {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
export interface ReleasePolicyRegistryPackage {
|
|
62
|
+
readonly error?: string | undefined;
|
|
62
63
|
readonly expectedTagVersion?: string | undefined;
|
|
63
64
|
readonly name: string;
|
|
64
65
|
readonly status: 'inaccessible' | 'missing' | 'published';
|
|
@@ -462,6 +463,7 @@ const readLabelFamily = <T extends string>(
|
|
|
462
463
|
const factsFromPolicyPackage = (
|
|
463
464
|
entry: ReleasePolicyRegistryPackage
|
|
464
465
|
): PackageRegistryFacts => ({
|
|
466
|
+
error: entry.error,
|
|
465
467
|
expectedTagVersion: entry.expectedTagVersion,
|
|
466
468
|
status: entry.status,
|
|
467
469
|
targetVersion: entry.version,
|
|
@@ -485,7 +487,7 @@ const registryBlockers = (
|
|
|
485
487
|
packages.flatMap((entry) => {
|
|
486
488
|
const state = classifyPackageRegistryState(factsFromPolicyPackage(entry));
|
|
487
489
|
if (state.kind === 'registry-inaccessible') {
|
|
488
|
-
return [`${entry.name}: registry state is inaccessible`];
|
|
490
|
+
return [`${entry.name}: registry state is inaccessible: ${state.error}`];
|
|
489
491
|
}
|
|
490
492
|
if (state.kind === 'tag-points-ahead') {
|
|
491
493
|
return [
|
|
@@ -986,6 +988,7 @@ const registryPackagesFromResults = (
|
|
|
986
988
|
};
|
|
987
989
|
}
|
|
988
990
|
return {
|
|
991
|
+
...(result.status === 'inaccessible' ? { error: result.error } : {}),
|
|
989
992
|
name: result.name,
|
|
990
993
|
status: result.status,
|
|
991
994
|
version: result.workspaceVersion,
|
|
@@ -30,7 +30,7 @@ export const getRetiredTopoCommandDiagnostic = (
|
|
|
30
30
|
|
|
31
31
|
return {
|
|
32
32
|
attempted,
|
|
33
|
-
message: `"${attempted}" was retired. Use "${replacement}" instead.\
|
|
33
|
+
message: `"${attempted}" was retired. Use "${replacement}" instead.\nTopography artifact commands now live at the top level: "trails compile", "trails validate", and "trails diff". "trails topo" is for topo-store history, pin, and unpin.`,
|
|
34
34
|
replacement,
|
|
35
35
|
};
|
|
36
36
|
};
|
package/src/run-watch.ts
CHANGED
|
@@ -27,7 +27,7 @@ import { watch as nodeWatch } from 'node:fs';
|
|
|
27
27
|
import type { FSWatcher } from 'node:fs';
|
|
28
28
|
import { dirname, extname } from 'node:path';
|
|
29
29
|
|
|
30
|
-
import type { TopoGraphEntry } from '@ontrails/
|
|
30
|
+
import type { TopoGraphEntry } from '@ontrails/topography';
|
|
31
31
|
|
|
32
32
|
// ---------------------------------------------------------------------------
|
|
33
33
|
// Constants
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { deriveOutputMode, output } from '@ontrails/cli';
|
|
2
2
|
import type { ActionResultContext } from '@ontrails/cli';
|
|
3
|
-
|
|
4
|
-
import
|
|
3
|
+
|
|
4
|
+
import { outlineOutputSchema } from './trails/wayfind-outline.js';
|
|
5
|
+
import type {
|
|
6
|
+
OutlineFeature,
|
|
7
|
+
OutlineOutput,
|
|
8
|
+
} from './trails/wayfind-outline.js';
|
|
5
9
|
|
|
6
10
|
const includesFeature = (
|
|
7
11
|
outline: OutlineOutput,
|