@agentskit/doc-bridge 1.6.4 → 1.7.44
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 +243 -0
- package/action.yml +1 -1
- package/dist/cli/program.js +793 -137
- package/dist/cli/program.js.map +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.js +38 -2
- package/dist/config/index.js.map +1 -1
- package/dist/{index-DudNuwI5.d.ts → index-C2PCQSrB.d.ts} +216 -25
- package/dist/index.d.ts +837 -67
- package/dist/index.js +858 -127
- package/dist/index.js.map +1 -1
- package/docs/PRD-enterprise-hardening.md +288 -0
- package/docs/adr/0001-enterprise-verification-contract.md +35 -0
- package/docs/knowledge-engine-runbook.md +18 -2
- package/docs/spec/analyzer-plugin-v1.md +24 -0
- package/docs/spec/benchmark-v1.md +30 -0
- package/docs/spec/config-v1.md +111 -0
- package/docs/validation-cycle-plan.md +236 -0
- package/docs/verification-harness.md +33 -4
- package/mcpb/manifest.json +1 -1
- package/package.json +1 -1
- package/scripts/report-visual-check.mjs +45 -10
- package/scripts/verification-harness.mjs +216 -13
- package/skills/doc-bridge-handoff/scripts/resolve-handoff.mjs +1 -1
- package/src/agents/registry-adapter.ts +31 -7
- package/src/cli/program.ts +44 -11
- package/src/config/index.ts +2 -0
- package/src/config/schema.ts +56 -0
- package/src/discovery/documentation.ts +46 -5
- package/src/discovery/repository.ts +95 -16
- package/src/index.ts +29 -0
- package/src/metrics/benchmark.ts +176 -0
- package/src/plugins/contract.ts +89 -0
- package/src/reconciliation/reconcile.ts +137 -3
- package/src/report/html.ts +302 -78
- package/src/schemas/knowledge.ts +16 -1
- package/src/version.ts +1 -1
- package/src/workflow/engine.ts +65 -9
package/src/schemas/knowledge.ts
CHANGED
|
@@ -51,11 +51,12 @@ export const EvidenceSchema = z
|
|
|
51
51
|
})
|
|
52
52
|
export type Evidence = z.infer<typeof EvidenceSchema>
|
|
53
53
|
|
|
54
|
-
export const CoverageStatusSchema = z.enum(['complete', 'partial', 'not-analyzed'])
|
|
54
|
+
export const CoverageStatusSchema = z.enum(['complete', 'partial', 'not-analyzed', 'not-applicable'])
|
|
55
55
|
|
|
56
56
|
export const CoverageSchema = z
|
|
57
57
|
.object({
|
|
58
58
|
analyzer: boundedString(128),
|
|
59
|
+
analyzerVersion: boundedString(64).optional(),
|
|
59
60
|
scope: boundedString(512),
|
|
60
61
|
status: CoverageStatusSchema,
|
|
61
62
|
reason: z.string().max(1_024).optional(),
|
|
@@ -165,7 +166,21 @@ export const ReconciliationReportV1Schema = z
|
|
|
165
166
|
entityCount: z.number().int().nonnegative(),
|
|
166
167
|
relationCount: z.number().int().nonnegative(),
|
|
167
168
|
diagnosticCount: z.number().int().nonnegative(),
|
|
169
|
+
scope: z.enum(['file', 'module', 'package']).optional(),
|
|
168
170
|
requiredRelationKinds: z.array(boundedString(128)).max(128).optional(),
|
|
171
|
+
diagnosticsByCode: z.record(z.string().max(128), z.number().int().nonnegative()).optional(),
|
|
172
|
+
diagnosticsByStatus: z.record(z.string().max(128), z.number().int().nonnegative()).optional(),
|
|
173
|
+
documentation: z.object({
|
|
174
|
+
documentCount: z.number().int().nonnegative(),
|
|
175
|
+
documentedDocumentCount: z.number().int().nonnegative(),
|
|
176
|
+
packageCount: z.number().int().nonnegative(),
|
|
177
|
+
packageStatus: z.object({
|
|
178
|
+
fresh: z.number().int().nonnegative(),
|
|
179
|
+
stale: z.number().int().nonnegative(),
|
|
180
|
+
missing: z.number().int().nonnegative(),
|
|
181
|
+
unverified: z.number().int().nonnegative(),
|
|
182
|
+
}).strict(),
|
|
183
|
+
}).strict().optional(),
|
|
169
184
|
})
|
|
170
185
|
.strict(),
|
|
171
186
|
})
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const PACKAGE_VERSION = '1.
|
|
1
|
+
export const PACKAGE_VERSION = '1.7.44'
|
package/src/workflow/engine.ts
CHANGED
|
@@ -22,10 +22,13 @@ export type WorkflowOptions = {
|
|
|
22
22
|
readonly sourceRevision: string
|
|
23
23
|
readonly configurationHash: string
|
|
24
24
|
readonly toolVersion?: string
|
|
25
|
+
readonly pipelineVersion?: string
|
|
26
|
+
readonly analyzerVersions?: Readonly<Record<string, string>>
|
|
25
27
|
readonly runId?: string
|
|
26
28
|
readonly stage?: WorkflowStage | 'all'
|
|
27
29
|
readonly inputs?: Partial<Record<WorkflowStage, unknown>>
|
|
28
30
|
readonly handlers: Partial<Record<WorkflowStage, WorkflowStageHandler>>
|
|
31
|
+
readonly shouldCancel?: () => boolean
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
export type WorkflowExecutionResult = {
|
|
@@ -67,6 +70,22 @@ const appendTransition = (stateDir: string, transition: WorkflowRunV1['transitio
|
|
|
67
70
|
}
|
|
68
71
|
|
|
69
72
|
const transition = (run: WorkflowRunV1, to: WorkflowState, reason?: string): WorkflowRunV1 => {
|
|
73
|
+
const allowed: Readonly<Record<WorkflowState, readonly WorkflowState[]>> = {
|
|
74
|
+
created: ['created', 'discovering', 'failed', 'cancelled'],
|
|
75
|
+
discovering: ['discovering', 'analyzed', 'failed', 'cancelled', 'stale'],
|
|
76
|
+
analyzed: ['analyzed', 'compared', 'failed', 'cancelled', 'stale'],
|
|
77
|
+
compared: ['compared', 'proposed', 'failed', 'cancelled', 'stale'],
|
|
78
|
+
'awaiting-agent': ['awaiting-agent', 'proposed', 'failed', 'cancelled', 'stale'],
|
|
79
|
+
proposed: ['proposed', 'validating', 'delivered', 'failed', 'cancelled', 'stale'],
|
|
80
|
+
'awaiting-approval': ['awaiting-approval', 'validating', 'failed', 'cancelled', 'stale'],
|
|
81
|
+
validating: ['validating', 'delivered', 'failed', 'cancelled', 'stale'],
|
|
82
|
+
delivered: ['delivered', 'stale', 'failed', 'cancelled'],
|
|
83
|
+
failed: ['failed', 'discovering', 'analyzed', 'compared', 'proposed', 'validating', 'delivered', 'cancelled'],
|
|
84
|
+
cancelled: ['cancelled', 'discovering', 'analyzed', 'compared', 'proposed', 'validating', 'delivered'],
|
|
85
|
+
stale: [],
|
|
86
|
+
superseded: [],
|
|
87
|
+
}
|
|
88
|
+
if (!allowed[run.state].includes(to)) throw new Error(`Illegal workflow transition ${run.state} -> ${to}.`)
|
|
70
89
|
const item = {
|
|
71
90
|
from: run.state,
|
|
72
91
|
to,
|
|
@@ -79,18 +98,30 @@ const transition = (run: WorkflowRunV1, to: WorkflowState, reason?: string): Wor
|
|
|
79
98
|
const runId = (): string => `${Date.now()}-${process.pid}`
|
|
80
99
|
|
|
81
100
|
const stageInputHash = (options: WorkflowOptions, stage: WorkflowStage, input: unknown): string =>
|
|
82
|
-
sha256NormalizedV1({ stage, input, sourceRevision: options.sourceRevision, configurationHash: options.configurationHash, toolVersion: options.toolVersion ?? '1.0.0' })
|
|
101
|
+
sha256NormalizedV1({ stage, input, sourceRevision: options.sourceRevision, configurationHash: options.configurationHash, pipelineVersion: options.pipelineVersion ?? '1.0.0', analyzerVersions: options.analyzerVersions ?? {}, toolVersion: options.toolVersion ?? '1.0.0' })
|
|
83
102
|
|
|
84
103
|
const stageArtifactPath = (stateDir: string, stage: WorkflowStage, inputHash: string): string => join(stateDir, 'artifacts', `${stage}-${inputHash}.json`)
|
|
85
104
|
|
|
86
105
|
const readArtifact = (path: string): PersistedArtifact => JSON.parse(readFileSync(path, 'utf8')) as PersistedArtifact
|
|
87
106
|
|
|
88
|
-
const
|
|
107
|
+
const readVerifiedArtifact = (path: string, stage: WorkflowStage, step: WorkflowStep): PersistedArtifact => {
|
|
108
|
+
const artifact = readArtifact(path)
|
|
109
|
+
if (artifact.type !== 'workflow-step-artifact' || artifact.stage !== stage || artifact.inputHash !== step.inputHash) throw new Error(`Invalid workflow artifact for stage "${stage}".`)
|
|
110
|
+
if (sha256NormalizedV1(artifact.value) !== artifact.outputHash || artifact.outputHash !== step.outputHash) throw new Error(`Workflow artifact hash mismatch for stage "${stage}".`)
|
|
111
|
+
return artifact
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const stepArtifactPath = (stateDir: string, step: WorkflowStep): string => {
|
|
115
|
+
const path = resolve(stateDir, step.artifactRefs?.[0] ?? '')
|
|
116
|
+
const pathRelativeToState = relative(stateDir, path)
|
|
117
|
+
if (pathRelativeToState.startsWith('..') || pathRelativeToState.startsWith('/')) throw new Error(`Workflow artifact escapes state directory for stage "${step.name}".`)
|
|
118
|
+
return path
|
|
119
|
+
}
|
|
89
120
|
|
|
90
121
|
const stepOutput = (stateDir: string, run: WorkflowRunV1, stage: WorkflowStage): unknown => {
|
|
91
122
|
const step = run.steps.find((item) => item.name === stage)
|
|
92
123
|
if (!step || step.status !== 'completed' || !step.artifactRefs?.[0]) return null
|
|
93
|
-
return
|
|
124
|
+
return readVerifiedArtifact(stepArtifactPath(stateDir, step), stage, step).value
|
|
94
125
|
}
|
|
95
126
|
|
|
96
127
|
const acquireLock = (stateDir: string): (() => void) => {
|
|
@@ -120,7 +151,7 @@ const loadManifest = (stateDir: string): WorkflowRunV1 | undefined => {
|
|
|
120
151
|
}
|
|
121
152
|
|
|
122
153
|
const baseRun = (options: WorkflowOptions, stateDir: string, supersedes?: string): WorkflowRunV1 => {
|
|
123
|
-
const inputHash = sha256NormalizedV1({ sourceRevision: options.sourceRevision, configurationHash: options.configurationHash, toolVersion: options.toolVersion ?? '1.0.0' })
|
|
154
|
+
const inputHash = sha256NormalizedV1({ sourceRevision: options.sourceRevision, configurationHash: options.configurationHash, pipelineVersion: options.pipelineVersion ?? '1.0.0', analyzerVersions: options.analyzerVersions ?? {}, toolVersion: options.toolVersion ?? '1.0.0' })
|
|
124
155
|
return WorkflowRunV1Schema.parse({
|
|
125
156
|
type: 'workflow-run',
|
|
126
157
|
schemaVersion: 1,
|
|
@@ -130,8 +161,8 @@ const baseRun = (options: WorkflowOptions, stateDir: string, supersedes?: string
|
|
|
130
161
|
sourceRevision: options.sourceRevision,
|
|
131
162
|
sourceRevisionKind: 'content',
|
|
132
163
|
configurationHash: options.configurationHash,
|
|
133
|
-
pipelineVersion: '1.0.0',
|
|
134
|
-
analyzerVersions: { workflow: options.toolVersion ?? '1.0.0' },
|
|
164
|
+
pipelineVersion: options.pipelineVersion ?? '1.0.0',
|
|
165
|
+
analyzerVersions: { ...(options.analyzerVersions ?? {}), workflow: options.toolVersion ?? '1.0.0' },
|
|
135
166
|
runId: options.runId ?? runId(),
|
|
136
167
|
state: 'created',
|
|
137
168
|
steps: WORKFLOW_STAGES.map((name) => ({ name, status: 'pending', inputHash })) as WorkflowStep[],
|
|
@@ -145,7 +176,8 @@ const withHash = (run: WorkflowRunV1): WorkflowRunV1 => WorkflowRunV1Schema.pars
|
|
|
145
176
|
const sameInputs = (run: WorkflowRunV1, options: WorkflowOptions): boolean =>
|
|
146
177
|
run.sourceRevision === options.sourceRevision &&
|
|
147
178
|
run.configurationHash === options.configurationHash &&
|
|
148
|
-
run.
|
|
179
|
+
run.pipelineVersion === (options.pipelineVersion ?? '1.0.0') &&
|
|
180
|
+
sha256NormalizedV1(run.analyzerVersions) === sha256NormalizedV1({ ...(options.analyzerVersions ?? {}), workflow: options.toolVersion ?? '1.0.0' })
|
|
149
181
|
|
|
150
182
|
const selectedStages = (stage: WorkflowOptions['stage']): readonly WorkflowStage[] =>
|
|
151
183
|
stage && stage !== 'all' ? [stage] : WORKFLOW_STAGES
|
|
@@ -174,7 +206,17 @@ export const runWorkflow = (options: WorkflowOptions): WorkflowExecutionResult =
|
|
|
174
206
|
if (!run) throw new Error('Workflow manifest was not initialized.')
|
|
175
207
|
const firstSelectedStage = selectedStages(options.stage)[0]
|
|
176
208
|
const previousStageIndex = firstSelectedStage ? WORKFLOW_STAGES.indexOf(firstSelectedStage) - 1 : -1
|
|
177
|
-
let previousOutput: unknown =
|
|
209
|
+
let previousOutput: unknown = null
|
|
210
|
+
if (previousStageIndex >= 0) {
|
|
211
|
+
try {
|
|
212
|
+
previousOutput = stepOutput(stateDir, run, WORKFLOW_STAGES[previousStageIndex]!)
|
|
213
|
+
} catch (error) {
|
|
214
|
+
run = withHash(transition(run, 'failed', error instanceof Error ? error.message : String(error)))
|
|
215
|
+
appendTransition(stateDir, run.transitions[run.transitions.length - 1]!)
|
|
216
|
+
writeManifest(stateDir, run)
|
|
217
|
+
throw error
|
|
218
|
+
}
|
|
219
|
+
}
|
|
178
220
|
const reusedStages: WorkflowStage[] = []
|
|
179
221
|
for (const stage of selectedStages(options.stage)) {
|
|
180
222
|
const input = options.inputs?.[stage] ?? previousOutput
|
|
@@ -182,11 +224,25 @@ export const runWorkflow = (options: WorkflowOptions): WorkflowExecutionResult =
|
|
|
182
224
|
const existing = run.steps.find((step) => step.name === stage)
|
|
183
225
|
const artifactPath = existing?.artifactRefs?.[0] ? resolve(stateDir, existing.artifactRefs[0]) : stageArtifactPath(stateDir, stage, inputHash)
|
|
184
226
|
if (existing?.status === 'completed' && existing.inputHash === inputHash && existing.outputHash && existsSync(artifactPath)) {
|
|
185
|
-
|
|
227
|
+
try {
|
|
228
|
+
previousOutput = readVerifiedArtifact(artifactPath, stage, existing).value
|
|
229
|
+
} catch (error) {
|
|
230
|
+
run = withHash(transition(run, 'failed', error instanceof Error ? error.message : String(error)))
|
|
231
|
+
appendTransition(stateDir, run.transitions[run.transitions.length - 1]!)
|
|
232
|
+
writeManifest(stateDir, run)
|
|
233
|
+
throw error
|
|
234
|
+
}
|
|
186
235
|
reusedStages.push(stage)
|
|
187
236
|
continue
|
|
188
237
|
}
|
|
189
238
|
|
|
239
|
+
if (options.shouldCancel?.()) {
|
|
240
|
+
run = withHash(transition(run, 'cancelled', `Workflow cancellation requested before stage "${stage}".`))
|
|
241
|
+
appendTransition(stateDir, run.transitions[run.transitions.length - 1]!)
|
|
242
|
+
writeManifest(stateDir, run)
|
|
243
|
+
return { run, stateDir, reusedStages }
|
|
244
|
+
}
|
|
245
|
+
|
|
190
246
|
const handler = options.handlers[stage]
|
|
191
247
|
if (!handler) throw new Error(`No handler configured for workflow stage "${stage}".`)
|
|
192
248
|
run = withHash(transition(run, stageState[stage]))
|