onto-mcp 0.3.2 → 0.4.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/.onto/processes/reconstruct/actionable-ontology-seed-recomposition-design.md +447 -0
- package/.onto/processes/reconstruct/foundry-style-ontology-seed-contract.md +934 -0
- package/.onto/processes/reconstruct/reconstruct-boundary-contract.md +303 -725
- package/.onto/processes/reconstruct/reconstruct-contract-registry.yaml +1645 -0
- package/.onto/processes/reconstruct/reconstruct-execution-ux-contract.md +26 -22
- package/.onto/processes/reconstruct/source-profile-contract.md +49 -23
- package/.onto/processes/reconstruct/source-profiles/code.md +6 -3
- package/.onto/processes/reconstruct/source-profiles/database.md +5 -2
- package/.onto/processes/reconstruct/source-profiles/document.md +5 -2
- package/.onto/processes/reconstruct/source-profiles/spreadsheet.md +5 -4
- package/.onto/processes/review/review-execution-ux-contract.md +40 -0
- package/.onto/processes/shared/pipeline-execution-ledger-contract.md +26 -10
- package/.onto/processes/shared/target-material-kind-contract.md +29 -16
- package/AGENTS.md +6 -4
- package/README.md +135 -76
- package/dist/cli.js +8 -8
- package/dist/core-api/reconstruct-api.js +117 -31
- package/dist/core-api/review-api.js +47 -0
- package/dist/core-runtime/cli/codex-review-unit-executor.js +39 -2
- package/dist/core-runtime/cli/complete-review-session.js +2 -2
- package/dist/core-runtime/cli/mock-review-unit-executor.js +1 -1
- package/dist/core-runtime/cli/review-invoke.js +9 -9
- package/dist/core-runtime/cli/run-review-prompt-execution.js +39 -5
- package/dist/core-runtime/cli/spawn-watcher.js +266 -47
- package/dist/core-runtime/cli/start-review-session.js +3 -3
- package/dist/core-runtime/llm/llm-caller.js +11 -0
- package/dist/core-runtime/llm/llm-tool-loop.js +2 -0
- package/dist/core-runtime/observability/runtime-stream-observation.js +118 -0
- package/dist/core-runtime/onboard/cli-host.js +149 -0
- package/dist/core-runtime/onboard/host-target.js +22 -0
- package/dist/core-runtime/onboard/json-config-host.js +122 -0
- package/dist/core-runtime/onboard/path-scan.js +26 -0
- package/dist/core-runtime/onboard/prompt.js +51 -0
- package/dist/core-runtime/onboard/register.js +207 -0
- package/dist/core-runtime/onboard/types.js +27 -0
- package/dist/core-runtime/reconstruct/actionable-seed-validation.js +1777 -0
- package/dist/core-runtime/reconstruct/artifact-types.js +10 -4
- package/dist/core-runtime/reconstruct/contract-registry.js +623 -0
- package/dist/core-runtime/reconstruct/domain-id.js +10 -0
- package/dist/core-runtime/reconstruct/governing-snapshot.js +716 -0
- package/dist/core-runtime/reconstruct/material-profile-validation.js +191 -0
- package/dist/core-runtime/reconstruct/materialize-preparation.js +49 -11
- package/dist/core-runtime/reconstruct/pipeline-execution-ledger.js +269 -79
- package/dist/core-runtime/reconstruct/post-seed-validation.js +1194 -51
- package/dist/core-runtime/reconstruct/record.js +104 -20
- package/dist/core-runtime/reconstruct/run.js +2107 -413
- package/dist/core-runtime/reconstruct/seed-claim-projections.js +268 -0
- package/dist/core-runtime/reconstruct/source-profiles.js +93 -4
- package/dist/core-runtime/reconstruct/terminal-validation.js +807 -0
- package/dist/core-runtime/review/review-invocation-runner.js +4 -4
- package/dist/mcp/server.js +110 -38
- package/dist/mcp/tool-schemas.js +20 -6
- package/package.json +8 -17
- package/scripts/onto-review-watch.sh +486 -0
- package/scripts/onto-runtime-watch.sh +122 -0
- package/scripts/postinstall-hint.js +22 -0
- package/.onto/processes/reconstruct/top-level-concept-discovery-contract.md +0 -387
- package/dist/core-runtime/cli/bootstrap-review-binding.js +0 -186
- package/dist/core-runtime/cli/codex-nested-dispatch.test.js +0 -390
- package/dist/core-runtime/cli/codex-nested-teamlead-executor.test.js +0 -335
- package/dist/core-runtime/cli/coordinator-helpers.js +0 -583
- package/dist/core-runtime/cli/coordinator-state-machine-deliberation.test.js +0 -167
- package/dist/core-runtime/cli/coordinator-state-machine.js +0 -794
- package/dist/core-runtime/cli/e2e-codex-multi-agent-fixes.test.js +0 -615
- package/dist/core-runtime/cli/e2e-start-review-session.test.js +0 -312
- package/dist/core-runtime/cli/health.js +0 -44
- package/dist/core-runtime/cli/inline-http-review-unit-executor.test.js +0 -567
- package/dist/core-runtime/cli/materialize-review-execution-preparation.js +0 -104
- package/dist/core-runtime/cli/migrate-session-roots.js +0 -118
- package/dist/core-runtime/cli/repo-layout-migration-replace.smoke.test.js +0 -106
- package/dist/core-runtime/cli/review-invoke-auto-resolution.test.js +0 -268
- package/dist/core-runtime/cli/review-invoke-coordinator-topology.test.js +0 -136
- package/dist/core-runtime/cli/review-invoke-resolver-caching.test.js +0 -201
- package/dist/core-runtime/cli/review-invoke-topology-dispatch.test.js +0 -192
- package/dist/core-runtime/cli/session-root-guard.js +0 -168
- package/dist/core-runtime/cli/spawn-watcher.test.js +0 -457
- package/dist/core-runtime/cli/strip-wrapping-code-fence.test.js +0 -79
- package/dist/core-runtime/cli/teamcreate-lens-deliberation-executor.js +0 -412
- package/dist/core-runtime/cli/teamcreate-lens-deliberation-executor.test.js +0 -351
- package/dist/core-runtime/cli/topology-executor-mapping.js +0 -139
- package/dist/core-runtime/cli/topology-executor-mapping.test.js +0 -173
- package/dist/core-runtime/cli/write-review-interpretation.js +0 -81
- package/dist/core-runtime/config/onto-config-cli.js +0 -278
- package/dist/core-runtime/config/onto-config-key-path.js +0 -288
- package/dist/core-runtime/config/onto-config-key-path.test.js +0 -195
- package/dist/core-runtime/config/onto-config-preview.js +0 -108
- package/dist/core-runtime/config/onto-config-preview.test.js +0 -132
- package/dist/core-runtime/discovery/config-chain.js +0 -118
- package/dist/core-runtime/discovery/config-chain.test.js +0 -103
- package/dist/core-runtime/discovery/config-profile.js +0 -199
- package/dist/core-runtime/discovery/config-profile.test.js +0 -233
- package/dist/core-runtime/discovery/host-detection.test.js +0 -186
- package/dist/core-runtime/discovery/installation-paths.test.js +0 -65
- package/dist/core-runtime/discovery/lens-registry.test.js +0 -81
- package/dist/core-runtime/discovery/path-normalization.test.js +0 -22
- package/dist/core-runtime/discovery/plugin-path.js +0 -72
- package/dist/core-runtime/discovery/plugin-path.test.js +0 -95
- package/dist/core-runtime/evolve/adapters/code-product/compile/compile-defense.js +0 -344
- package/dist/core-runtime/evolve/adapters/code-product/compile/compile-defense.test.js +0 -915
- package/dist/core-runtime/evolve/adapters/code-product/compile/compile.js +0 -564
- package/dist/core-runtime/evolve/adapters/code-product/compile/compile.test.js +0 -708
- package/dist/core-runtime/evolve/adapters/code-product/parsers/brief-parser.js +0 -165
- package/dist/core-runtime/evolve/adapters/code-product/parsers/brief-parser.test.js +0 -227
- package/dist/core-runtime/evolve/adapters/code-product/validators/validate.js +0 -59
- package/dist/core-runtime/evolve/adapters/code-product/validators/validate.test.js +0 -205
- package/dist/core-runtime/evolve/adapters/methodology/adapter.js +0 -16
- package/dist/core-runtime/evolve/adapters/methodology/adapter.test.js +0 -9
- package/dist/core-runtime/evolve/adapters/methodology/perspectives/authority-consistency.js +0 -298
- package/dist/core-runtime/evolve/adapters/methodology/perspectives/authority-consistency.test.js +0 -70
- package/dist/core-runtime/evolve/adapters/methodology/scope-types/process.js +0 -46
- package/dist/core-runtime/evolve/adapters/methodology/scope-types/process.test.js +0 -73
- package/dist/core-runtime/evolve/adapters/registry.js +0 -47
- package/dist/core-runtime/evolve/adapters/registry.test.js +0 -67
- package/dist/core-runtime/evolve/cli.js +0 -256
- package/dist/core-runtime/evolve/commands/align.js +0 -194
- package/dist/core-runtime/evolve/commands/align.test.js +0 -82
- package/dist/core-runtime/evolve/commands/apply.js +0 -161
- package/dist/core-runtime/evolve/commands/apply.test.js +0 -138
- package/dist/core-runtime/evolve/commands/close.js +0 -39
- package/dist/core-runtime/evolve/commands/close.test.js +0 -99
- package/dist/core-runtime/evolve/commands/defer.js +0 -40
- package/dist/core-runtime/evolve/commands/defer.test.js +0 -134
- package/dist/core-runtime/evolve/commands/draft.js +0 -323
- package/dist/core-runtime/evolve/commands/draft.test.js +0 -178
- package/dist/core-runtime/evolve/commands/e2e-evolve-full-cycle.test.js +0 -208
- package/dist/core-runtime/evolve/commands/error-messages.js +0 -125
- package/dist/core-runtime/evolve/commands/error-messages.test.js +0 -167
- package/dist/core-runtime/evolve/commands/propose-align.js +0 -222
- package/dist/core-runtime/evolve/commands/propose-align.test.js +0 -136
- package/dist/core-runtime/evolve/commands/reconstruct.js +0 -330
- package/dist/core-runtime/evolve/commands/reconstruct.test.js +0 -278
- package/dist/core-runtime/evolve/commands/shared.js +0 -22
- package/dist/core-runtime/evolve/commands/stale-check.js +0 -103
- package/dist/core-runtime/evolve/commands/stale-check.test.js +0 -84
- package/dist/core-runtime/evolve/commands/start.js +0 -887
- package/dist/core-runtime/evolve/commands/start.test.js +0 -396
- package/dist/core-runtime/evolve/config/project-config.js +0 -99
- package/dist/core-runtime/evolve/config/project-config.test.js +0 -170
- package/dist/core-runtime/evolve/renderers/align-packet.js +0 -280
- package/dist/core-runtime/evolve/renderers/align-packet.test.js +0 -332
- package/dist/core-runtime/evolve/renderers/draft-packet.js +0 -303
- package/dist/core-runtime/evolve/renderers/draft-packet.test.js +0 -377
- package/dist/core-runtime/evolve/renderers/format.js +0 -5
- package/dist/core-runtime/evolve/renderers/scope-md.js +0 -237
- package/dist/core-runtime/evolve/renderers/scope-md.test.js +0 -306
- package/dist/core-runtime/govern/cli.js +0 -369
- package/dist/core-runtime/govern/cli.test.js +0 -314
- package/dist/core-runtime/govern/drift-engine.js +0 -103
- package/dist/core-runtime/govern/drift-engine.test.js +0 -319
- package/dist/core-runtime/govern/promote-principle.js +0 -206
- package/dist/core-runtime/govern/promote-principle.test.js +0 -368
- package/dist/core-runtime/govern/queue.js +0 -81
- package/dist/core-runtime/govern/types.js +0 -16
- package/dist/core-runtime/install/cli.js +0 -530
- package/dist/core-runtime/install/detect.js +0 -128
- package/dist/core-runtime/install/detect.test.js +0 -155
- package/dist/core-runtime/install/gitignore-update.js +0 -74
- package/dist/core-runtime/install/gitignore-update.test.js +0 -64
- package/dist/core-runtime/install/install-integration.test.js +0 -373
- package/dist/core-runtime/install/prompts.js +0 -389
- package/dist/core-runtime/install/prompts.test.js +0 -293
- package/dist/core-runtime/install/types.js +0 -26
- package/dist/core-runtime/install/validation.js +0 -295
- package/dist/core-runtime/install/validation.test.js +0 -313
- package/dist/core-runtime/install/writer.js +0 -254
- package/dist/core-runtime/install/writer.test.js +0 -218
- package/dist/core-runtime/learning/extractor.js +0 -461
- package/dist/core-runtime/learning/feedback.js +0 -179
- package/dist/core-runtime/learning/health-report.js +0 -165
- package/dist/core-runtime/learning/health-report.test.js +0 -169
- package/dist/core-runtime/learning/loader.js +0 -388
- package/dist/core-runtime/learning/loader.test.js +0 -102
- package/dist/core-runtime/learning/promote/apply-state.js +0 -240
- package/dist/core-runtime/learning/promote/audit-obligation.js +0 -195
- package/dist/core-runtime/learning/promote/collector.js +0 -432
- package/dist/core-runtime/learning/promote/degraded-state.js +0 -125
- package/dist/core-runtime/learning/promote/domain-doc-proposer.js +0 -166
- package/dist/core-runtime/learning/promote/e2e-promote.test.js +0 -6385
- package/dist/core-runtime/learning/promote/health-snapshot.js +0 -150
- package/dist/core-runtime/learning/promote/insight-reclassifier.js +0 -544
- package/dist/core-runtime/learning/promote/judgment-auditor.js +0 -517
- package/dist/core-runtime/learning/promote/panel-reviewer.js +0 -1158
- package/dist/core-runtime/learning/promote/promote-executor.js +0 -1675
- package/dist/core-runtime/learning/promote/promoter.js +0 -307
- package/dist/core-runtime/learning/promote/retirement.js +0 -122
- package/dist/core-runtime/learning/promote/types.js +0 -23
- package/dist/core-runtime/learning/prompt-sections.js +0 -51
- package/dist/core-runtime/learning/shared/artifact-registry-init.js +0 -45
- package/dist/core-runtime/learning/shared/artifact-registry.js +0 -254
- package/dist/core-runtime/learning/shared/audit-obligation-kernel.js +0 -73
- package/dist/core-runtime/learning/shared/audit-state.js +0 -99
- package/dist/core-runtime/learning/shared/duplicate-check.js +0 -28
- package/dist/core-runtime/learning/shared/llm-caller.js +0 -831
- package/dist/core-runtime/learning/shared/llm-caller.test.js +0 -601
- package/dist/core-runtime/learning/shared/llm-tool-loop.js +0 -393
- package/dist/core-runtime/learning/shared/mode.js +0 -25
- package/dist/core-runtime/learning/shared/paths.js +0 -84
- package/dist/core-runtime/learning/shared/paths.test.js +0 -79
- package/dist/core-runtime/learning/shared/patterns.js +0 -37
- package/dist/core-runtime/learning/shared/recoverability.js +0 -355
- package/dist/core-runtime/learning/shared/recovery-context.js +0 -374
- package/dist/core-runtime/learning/shared/scope.js +0 -1
- package/dist/core-runtime/learning/shared/semantic-classifier.js +0 -94
- package/dist/core-runtime/learning/shared/specs/apply-execution-state-spec.js +0 -42
- package/dist/core-runtime/learning/shared/specs/audit-state-spec.js +0 -37
- package/dist/core-runtime/learning/shared/specs/backup-metadata-spec.js +0 -39
- package/dist/core-runtime/learning/shared/specs/emergency-log-spec.js +0 -41
- package/dist/core-runtime/learning/shared/specs/layout-version-spec.js +0 -38
- package/dist/core-runtime/learning/shared/specs/promote-decisions-spec.js +0 -43
- package/dist/core-runtime/learning/shared/specs/promote-report-spec.js +0 -113
- package/dist/core-runtime/learning/shared/specs/prune-log-spec.js +0 -36
- package/dist/core-runtime/learning/shared/specs/recovery-resolution-spec.js +0 -48
- package/dist/core-runtime/learning/shared/specs/restore-manifest-spec.js +0 -43
- package/dist/core-runtime/learning/shared/specs/spec-helpers.js +0 -64
- package/dist/core-runtime/learning/usage-tracker.js +0 -190
- package/dist/core-runtime/learning/usage-tracker.test.js +0 -176
- package/dist/core-runtime/onboard/detect-review-axes.js +0 -122
- package/dist/core-runtime/onboard/detect-review-axes.test.js +0 -127
- package/dist/core-runtime/onboard/write-review-block.js +0 -188
- package/dist/core-runtime/onboard/write-review-block.test.js +0 -240
- package/dist/core-runtime/readers/brownfield-builder.js +0 -150
- package/dist/core-runtime/readers/brownfield-builder.test.js +0 -136
- package/dist/core-runtime/readers/code-chunk-collector.js +0 -53
- package/dist/core-runtime/readers/code-chunk-collector.test.js +0 -136
- package/dist/core-runtime/readers/file-utils.js +0 -240
- package/dist/core-runtime/readers/file-utils.test.js +0 -146
- package/dist/core-runtime/readers/lexicon-citation-check.js +0 -93
- package/dist/core-runtime/readers/lexicon-citation-check.test.js +0 -77
- package/dist/core-runtime/readers/mcp-figma.js +0 -30
- package/dist/core-runtime/readers/mcp-figma.test.js +0 -82
- package/dist/core-runtime/readers/mcp-generic.js +0 -31
- package/dist/core-runtime/readers/mcp-generic.test.js +0 -76
- package/dist/core-runtime/readers/ontology-index.js +0 -148
- package/dist/core-runtime/readers/ontology-index.test.js +0 -245
- package/dist/core-runtime/readers/ontology-query.js +0 -168
- package/dist/core-runtime/readers/ontology-query.test.js +0 -311
- package/dist/core-runtime/readers/ontology-resolve.js +0 -48
- package/dist/core-runtime/readers/ontology-resolve.test.js +0 -48
- package/dist/core-runtime/readers/patterns/index.js +0 -7
- package/dist/core-runtime/readers/review-log.js +0 -213
- package/dist/core-runtime/readers/review-log.test.js +0 -313
- package/dist/core-runtime/readers/scan-local.js +0 -102
- package/dist/core-runtime/readers/scan-local.test.js +0 -102
- package/dist/core-runtime/readers/scan-tarball.js +0 -121
- package/dist/core-runtime/readers/scan-tarball.test.js +0 -283
- package/dist/core-runtime/readers/scan-vault.js +0 -34
- package/dist/core-runtime/readers/scan-vault.test.js +0 -81
- package/dist/core-runtime/readers/types.js +0 -42
- package/dist/core-runtime/readers/types.test.js +0 -94
- package/dist/core-runtime/readers/viewpoint-collectors.js +0 -229
- package/dist/core-runtime/reconstruct/seed-candidate-validation.js +0 -385
- package/dist/core-runtime/review/citation-audit.test.js +0 -165
- package/dist/core-runtime/review/execution-plan-resolver.js +0 -247
- package/dist/core-runtime/review/execution-plan-resolver.test.js +0 -243
- package/dist/core-runtime/review/execution-topology-resolver-axis-first.test.js +0 -246
- package/dist/core-runtime/review/execution-topology-resolver.js +0 -401
- package/dist/core-runtime/review/execution-topology-resolver.test.js +0 -315
- package/dist/core-runtime/review/inline-context-embedder.test.js +0 -154
- package/dist/core-runtime/review/legacy-mode-policy.js +0 -88
- package/dist/core-runtime/review/materializers-effort-persist.test.js +0 -79
- package/dist/core-runtime/review/ontology-path-classifier.js +0 -179
- package/dist/core-runtime/review/ontology-path-classifier.test.js +0 -216
- package/dist/core-runtime/review/packet-boundary-policy.test.js +0 -107
- package/dist/core-runtime/review/participating-lens-paths.test.js +0 -73
- package/dist/core-runtime/review/review-config-legacy-translate.js +0 -244
- package/dist/core-runtime/review/review-config-legacy-translate.test.js +0 -161
- package/dist/core-runtime/review/review-config-validator.js +0 -289
- package/dist/core-runtime/review/review-config-validator.test.js +0 -236
- package/dist/core-runtime/review/shape-pipeline-audit.test.js +0 -311
- package/dist/core-runtime/review/shape-to-topology-id.js +0 -117
- package/dist/core-runtime/review/shape-to-topology-id.test.js +0 -132
- package/dist/core-runtime/review/topology-shape-derivation.js +0 -155
- package/dist/core-runtime/review/topology-shape-derivation.test.js +0 -195
- package/dist/core-runtime/scope-runtime/constants.js +0 -12
- package/dist/core-runtime/scope-runtime/constraint-pool.js +0 -166
- package/dist/core-runtime/scope-runtime/constraint-pool.test.js +0 -674
- package/dist/core-runtime/scope-runtime/domain-validation-log.js +0 -135
- package/dist/core-runtime/scope-runtime/domain-validation-log.test.js +0 -156
- package/dist/core-runtime/scope-runtime/eval-persistence.js +0 -65
- package/dist/core-runtime/scope-runtime/eval-persistence.test.js +0 -84
- package/dist/core-runtime/scope-runtime/event-pipeline.js +0 -64
- package/dist/core-runtime/scope-runtime/event-pipeline.test.js +0 -450
- package/dist/core-runtime/scope-runtime/event-store.js +0 -39
- package/dist/core-runtime/scope-runtime/event-store.test.js +0 -95
- package/dist/core-runtime/scope-runtime/gate-guard.js +0 -348
- package/dist/core-runtime/scope-runtime/gate-guard.test.js +0 -1047
- package/dist/core-runtime/scope-runtime/hash.js +0 -4
- package/dist/core-runtime/scope-runtime/hash.test.js +0 -33
- package/dist/core-runtime/scope-runtime/id.js +0 -4
- package/dist/core-runtime/scope-runtime/id.test.js +0 -17
- package/dist/core-runtime/scope-runtime/reducer.js +0 -297
- package/dist/core-runtime/scope-runtime/reducer.test.js +0 -759
- package/dist/core-runtime/scope-runtime/scope-manager.js +0 -161
- package/dist/core-runtime/scope-runtime/state-machine.js +0 -309
- package/dist/core-runtime/scope-runtime/state-machine.test.js +0 -704
- package/dist/core-runtime/scope-runtime/types.js +0 -116
- package/dist/core-runtime/scope-runtime/types.test.js +0 -69
- package/dist/core-runtime/translate/render-for-user.js +0 -169
- package/dist/core-runtime/translate/render-for-user.test.js +0 -122
- package/dist/providers/capability-contract.js +0 -1
|
@@ -1,461 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Learning extraction orchestrator — Phase 2.
|
|
3
|
-
*
|
|
4
|
-
* Executes the A-7~A-14 accumulation DAG + C-11 feedback markers.
|
|
5
|
-
* Design authority: Phase 2 설계 v4 (logical-snuggling-parrot.md)
|
|
6
|
-
*
|
|
7
|
-
* Principles:
|
|
8
|
-
* - Extraction is fail-safe (리뷰 차단 금지)
|
|
9
|
-
* - Validation is fail-close (quarantine)
|
|
10
|
-
* - Classification failure is fail-close (unclassified_pending — 저장 안 함)
|
|
11
|
-
* - Live storage gate: save + active only
|
|
12
|
-
*
|
|
13
|
-
* Phase 2 scope: append-only extraction. A-12r (conflict replace) and
|
|
14
|
-
* A-12rej (conflict reject) are intentionally deferred to Phase 3 promote
|
|
15
|
-
* per R1-U1 design decision. Conflict items are recorded in manifest
|
|
16
|
-
* ConflictProposal[] but NOT executed against live storage.
|
|
17
|
-
*/
|
|
18
|
-
import fs from "node:fs";
|
|
19
|
-
import path from "node:path";
|
|
20
|
-
import crypto from "node:crypto";
|
|
21
|
-
import { TAG_PATTERN, GUARDRAIL_PATTERN, RAW_LLM_LINE_RE, NEWLY_LEARNED_HEADER_RE, } from "./shared/patterns.js";
|
|
22
|
-
import { resolveWritePaths } from "./shared/paths.js";
|
|
23
|
-
import { extractContent, isContentDuplicate } from "./shared/duplicate-check.js";
|
|
24
|
-
import { classifyLearningItem } from "./shared/semantic-classifier.js";
|
|
25
|
-
import { callLlm } from "./shared/llm-caller.js";
|
|
26
|
-
import { parseEventMarkers, matchMarkersToLearnings, attachEventMarkers, } from "./feedback.js";
|
|
27
|
-
// ---------------------------------------------------------------------------
|
|
28
|
-
// Helpers
|
|
29
|
-
// ---------------------------------------------------------------------------
|
|
30
|
-
function generateLearningId(content) {
|
|
31
|
-
return crypto.createHash("sha256").update(content).digest("hex").slice(0, 12);
|
|
32
|
-
}
|
|
33
|
-
function isoDate() {
|
|
34
|
-
return new Date().toISOString().slice(0, 10);
|
|
35
|
-
}
|
|
36
|
-
function isoTimestamp() {
|
|
37
|
-
return new Date().toISOString();
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* A-7: Assemble source metadata and produce full §1.3 line.
|
|
41
|
-
*/
|
|
42
|
-
function assembleSourceMetadata(rawLine, projectRoot, sessionDomain) {
|
|
43
|
-
const project = path.basename(projectRoot);
|
|
44
|
-
const domain = sessionDomain ?? "none";
|
|
45
|
-
const date = isoDate();
|
|
46
|
-
// Raw LLM line ends with [impact:...] — insert source before it
|
|
47
|
-
const impactMatch = rawLine.match(/\s+\[impact:(high|normal)\]\s*$/);
|
|
48
|
-
if (impactMatch) {
|
|
49
|
-
const beforeImpact = rawLine.slice(0, impactMatch.index);
|
|
50
|
-
return `${beforeImpact} (source: ${project}, ${domain}, ${date}) ${impactMatch[0].trim()}`;
|
|
51
|
-
}
|
|
52
|
-
// Fallback: append source at the end
|
|
53
|
-
return `${rawLine.trimEnd()} (source: ${project}, ${domain}, ${date})`;
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Parse raw LLM lines from the ### Newly Learned section of a lens output.
|
|
57
|
-
*/
|
|
58
|
-
function parseNewlyLearnedSection(lensOutput) {
|
|
59
|
-
const headerMatch = lensOutput.match(NEWLY_LEARNED_HEADER_RE);
|
|
60
|
-
if (!headerMatch)
|
|
61
|
-
return [];
|
|
62
|
-
const sectionStart = headerMatch.index + headerMatch[0].length;
|
|
63
|
-
const rest = lensOutput.slice(sectionStart);
|
|
64
|
-
// Section ends at the next heading or end of string
|
|
65
|
-
const nextHeadingMatch = rest.match(/\n##[#]?\s/);
|
|
66
|
-
const sectionText = nextHeadingMatch
|
|
67
|
-
? rest.slice(0, nextHeadingMatch.index)
|
|
68
|
-
: rest;
|
|
69
|
-
const lines = sectionText.split("\n");
|
|
70
|
-
const rawLines = [];
|
|
71
|
-
for (const line of lines) {
|
|
72
|
-
const trimmed = line.trim();
|
|
73
|
-
if (!trimmed)
|
|
74
|
-
continue;
|
|
75
|
-
if (/^[-*+]\s+none\s*$/i.test(trimmed))
|
|
76
|
-
continue;
|
|
77
|
-
if (/^[-*+]\s+\[/.test(trimmed)) {
|
|
78
|
-
rawLines.push(trimmed);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
return rawLines;
|
|
82
|
-
}
|
|
83
|
-
// ---------------------------------------------------------------------------
|
|
84
|
-
// Repair helpers (A-8f, A-9f)
|
|
85
|
-
// ---------------------------------------------------------------------------
|
|
86
|
-
const REPAIR_SYSTEM_PROMPT = `You are a formatting repair assistant. Fix the learning item line to match the required format. Return ONLY the corrected line, nothing else.`;
|
|
87
|
-
async function repairTagFormat(rawLine, failureReason) {
|
|
88
|
-
try {
|
|
89
|
-
const userPrompt = `Original line: ${rawLine}
|
|
90
|
-
|
|
91
|
-
Failure: ${failureReason}
|
|
92
|
-
|
|
93
|
-
Required format: - [{fact|judgment}] [{methodology|domain/{name}}]+ [{guardrail|foundation|convention}]? {content} [impact:{high|normal}]
|
|
94
|
-
Rules: type required, applicability 1+, role optional (guardrail|foundation|convention only), impact required.
|
|
95
|
-
Fix the line and return ONLY the corrected line.`;
|
|
96
|
-
const result = await callLlm(REPAIR_SYSTEM_PROMPT, userPrompt, {
|
|
97
|
-
max_tokens: 256,
|
|
98
|
-
});
|
|
99
|
-
const repaired = result.text.trim();
|
|
100
|
-
if (RAW_LLM_LINE_RE.test(repaired))
|
|
101
|
-
return repaired;
|
|
102
|
-
return null;
|
|
103
|
-
}
|
|
104
|
-
catch {
|
|
105
|
-
return null;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
async function repairGuardrailFormat(rawLine) {
|
|
109
|
-
try {
|
|
110
|
-
const userPrompt = `Original line: ${rawLine}
|
|
111
|
-
|
|
112
|
-
This is a [guardrail] learning item but it's missing the required three elements.
|
|
113
|
-
Required: **Situation**: ... **Result**: ... **Corrective action**: ...
|
|
114
|
-
Add the missing elements to the content. Return ONLY the corrected line.`;
|
|
115
|
-
const result = await callLlm(REPAIR_SYSTEM_PROMPT, userPrompt, {
|
|
116
|
-
max_tokens: 512,
|
|
117
|
-
});
|
|
118
|
-
const repaired = result.text.trim();
|
|
119
|
-
if (GUARDRAIL_PATTERN.test(repaired))
|
|
120
|
-
return repaired;
|
|
121
|
-
return null;
|
|
122
|
-
}
|
|
123
|
-
catch {
|
|
124
|
-
return null;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
// ---------------------------------------------------------------------------
|
|
128
|
-
// Per-item pipeline
|
|
129
|
-
// ---------------------------------------------------------------------------
|
|
130
|
-
async function processItem(rawLine, lensId, config, existingLines, writePaths) {
|
|
131
|
-
// A-7: Assemble source metadata
|
|
132
|
-
const assembled = assembleSourceMetadata(rawLine, config.projectRoot, config.sessionDomain);
|
|
133
|
-
// A-8: TAG_PATTERN validation
|
|
134
|
-
if (!TAG_PATTERN.test(assembled)) {
|
|
135
|
-
// A-8f: Repair attempt (1x)
|
|
136
|
-
const repaired = await repairTagFormat(rawLine, "TAG_PATTERN validation failed");
|
|
137
|
-
if (repaired) {
|
|
138
|
-
const repairedAssembled = assembleSourceMetadata(repaired, config.projectRoot, config.sessionDomain);
|
|
139
|
-
if (TAG_PATTERN.test(repairedAssembled)) {
|
|
140
|
-
// Repaired — continue with repaired line
|
|
141
|
-
return processValidatedItem(repairedAssembled, rawLine, repaired, true, lensId, config, existingLines, writePaths);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
// A-8f2: Quarantine
|
|
145
|
-
const q8Trace = {
|
|
146
|
-
kind: "quarantined",
|
|
147
|
-
lens_id: lensId,
|
|
148
|
-
raw_line: rawLine,
|
|
149
|
-
assembled_line: assembled,
|
|
150
|
-
failure_stage: repaired ? "A-8f" : "A-8",
|
|
151
|
-
failure_reason: "TAG_PATTERN validation failed after repair attempt",
|
|
152
|
-
};
|
|
153
|
-
if (repaired)
|
|
154
|
-
q8Trace.repaired_line = repaired;
|
|
155
|
-
return { trace: q8Trace };
|
|
156
|
-
}
|
|
157
|
-
return processValidatedItem(assembled, rawLine, undefined, false, lensId, config, existingLines, writePaths);
|
|
158
|
-
}
|
|
159
|
-
async function processValidatedItem(assembled, rawLine, repairedLine, repaired, lensId, config, existingLines, writePaths) {
|
|
160
|
-
// A-9: GUARDRAIL_PATTERN check (guardrail only)
|
|
161
|
-
const hasGuardrail = /\[guardrail\]/.test(assembled);
|
|
162
|
-
if (hasGuardrail && !GUARDRAIL_PATTERN.test(assembled)) {
|
|
163
|
-
// A-9f: Repair attempt (1x)
|
|
164
|
-
const guardrailRepaired = await repairGuardrailFormat(rawLine);
|
|
165
|
-
if (guardrailRepaired) {
|
|
166
|
-
const grAssembled = assembleSourceMetadata(guardrailRepaired, config.projectRoot, config.sessionDomain);
|
|
167
|
-
if (TAG_PATTERN.test(grAssembled) && GUARDRAIL_PATTERN.test(grAssembled)) {
|
|
168
|
-
return processClassifiedItem(grAssembled, rawLine, guardrailRepaired, true, lensId, config, existingLines, writePaths);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
// A-9f2: Quarantine
|
|
172
|
-
const q9Trace = {
|
|
173
|
-
kind: "quarantined",
|
|
174
|
-
lens_id: lensId,
|
|
175
|
-
raw_line: rawLine,
|
|
176
|
-
assembled_line: assembled,
|
|
177
|
-
failure_stage: guardrailRepaired ? "A-9f" : "A-9",
|
|
178
|
-
failure_reason: "GUARDRAIL_PATTERN validation failed after repair attempt",
|
|
179
|
-
};
|
|
180
|
-
if (guardrailRepaired)
|
|
181
|
-
q9Trace.repaired_line = guardrailRepaired;
|
|
182
|
-
return { trace: q9Trace };
|
|
183
|
-
}
|
|
184
|
-
return processClassifiedItem(assembled, rawLine, repairedLine, repaired, lensId, config, existingLines, writePaths);
|
|
185
|
-
}
|
|
186
|
-
async function processClassifiedItem(assembled, rawLine, repairedLine, repaired, lensId, config, existingLines, writePaths) {
|
|
187
|
-
// A-10: Content-level dedup
|
|
188
|
-
const content = extractContent(assembled);
|
|
189
|
-
if (content && isContentDuplicate(content, existingLines)) {
|
|
190
|
-
return {
|
|
191
|
-
trace: buildClassifiedTrace({
|
|
192
|
-
assembled, rawLine, repaired, lensId,
|
|
193
|
-
decision: "duplicate_skip",
|
|
194
|
-
reason: "Exact content match with existing item.",
|
|
195
|
-
writePaths, mode: config.mode,
|
|
196
|
-
...(repairedLine !== undefined ? { repairedLine } : {}),
|
|
197
|
-
}),
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
// A-11: Semantic classification
|
|
201
|
-
const classification = await classifyLearningItem(assembled, existingLines);
|
|
202
|
-
const decision = classification.decision;
|
|
203
|
-
// Build trace base
|
|
204
|
-
const traceCtx = {
|
|
205
|
-
assembled, rawLine, repaired, lensId,
|
|
206
|
-
decision,
|
|
207
|
-
reason: classification.reason,
|
|
208
|
-
writePaths, mode: config.mode,
|
|
209
|
-
modelId: classification.model_id,
|
|
210
|
-
promptHash: classification.prompt_hash,
|
|
211
|
-
};
|
|
212
|
-
if (repairedLine !== undefined)
|
|
213
|
-
traceCtx.repairedLine = repairedLine;
|
|
214
|
-
if (classification.conflict_kind)
|
|
215
|
-
traceCtx.conflictKind = classification.conflict_kind;
|
|
216
|
-
if (classification.matched_existing_line)
|
|
217
|
-
traceCtx.matchedExistingLine = classification.matched_existing_line;
|
|
218
|
-
const trace = buildClassifiedTrace(traceCtx);
|
|
219
|
-
let conflictProposal;
|
|
220
|
-
if (decision === "conflict_propose_replace" ||
|
|
221
|
-
decision === "conflict_propose_keep" ||
|
|
222
|
-
decision === "conflict_propose_coexist") {
|
|
223
|
-
conflictProposal = {
|
|
224
|
-
lens_id: lensId,
|
|
225
|
-
new_item_line: assembled,
|
|
226
|
-
matched_existing_line: classification.matched_existing_line ?? "",
|
|
227
|
-
decision,
|
|
228
|
-
conflict_kind: classification.conflict_kind,
|
|
229
|
-
reason: classification.reason,
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
|
-
// A-13 + A-14: Write (save + active only)
|
|
233
|
-
if (decision === "save" && config.mode === "active") {
|
|
234
|
-
const learningId = generateLearningId(assembled);
|
|
235
|
-
const lineWithId = `${assembled}\n<!-- learning_id: ${learningId} taxonomy_version: phase2-v1 -->`;
|
|
236
|
-
try {
|
|
237
|
-
// Ensure directory exists
|
|
238
|
-
const dir = path.dirname(writePaths.write_path);
|
|
239
|
-
if (!fs.existsSync(dir)) {
|
|
240
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
241
|
-
}
|
|
242
|
-
// Ensure format_version marker exists
|
|
243
|
-
if (!fs.existsSync(writePaths.write_path)) {
|
|
244
|
-
fs.writeFileSync(writePaths.write_path, `<!-- format_version: 1 -->\n`, "utf8");
|
|
245
|
-
}
|
|
246
|
-
// Concurrency contract: single session writes to each agent file at a time.
|
|
247
|
-
// Concurrent sessions writing to the same agent file are not supported.
|
|
248
|
-
fs.appendFileSync(writePaths.write_path, `${lineWithId}\n`, "utf8");
|
|
249
|
-
trace.learning_id = learningId;
|
|
250
|
-
trace.persistence_result = "written";
|
|
251
|
-
}
|
|
252
|
-
catch (error) {
|
|
253
|
-
trace.persistence_result = "write_error";
|
|
254
|
-
trace.write_error =
|
|
255
|
-
error instanceof Error ? error.message : String(error);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
const result = { trace };
|
|
259
|
-
if (conflictProposal)
|
|
260
|
-
result.conflictProposal = conflictProposal;
|
|
261
|
-
return result;
|
|
262
|
-
}
|
|
263
|
-
function getPersistenceResult(decision, mode) {
|
|
264
|
-
switch (decision) {
|
|
265
|
-
case "save":
|
|
266
|
-
return mode === "active" ? "written" : "skipped_shadow";
|
|
267
|
-
case "duplicate_skip":
|
|
268
|
-
return "skipped_duplicate";
|
|
269
|
-
case "conflict_propose_replace":
|
|
270
|
-
case "conflict_propose_keep":
|
|
271
|
-
case "conflict_propose_coexist":
|
|
272
|
-
return "skipped_conflict";
|
|
273
|
-
case "unclassified_pending":
|
|
274
|
-
return "skipped_unclassified";
|
|
275
|
-
default: {
|
|
276
|
-
const _exhaustive = decision;
|
|
277
|
-
return "skipped_shadow";
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
function buildClassifiedTrace(ctx) {
|
|
282
|
-
const trace = {
|
|
283
|
-
kind: "classified",
|
|
284
|
-
lens_id: ctx.lensId,
|
|
285
|
-
raw_line: ctx.rawLine,
|
|
286
|
-
assembled_line: ctx.assembled,
|
|
287
|
-
repaired: ctx.repaired,
|
|
288
|
-
decision: ctx.decision,
|
|
289
|
-
reason: ctx.reason,
|
|
290
|
-
write_path: ctx.decision === "save" && ctx.mode === "active" ? ctx.writePaths.write_path : null,
|
|
291
|
-
write_scope: ctx.decision === "save" && ctx.mode === "active" ? ctx.writePaths.write_scope : null,
|
|
292
|
-
learning_id: null,
|
|
293
|
-
persistence_result: getPersistenceResult(ctx.decision, ctx.mode),
|
|
294
|
-
model_id: ctx.modelId ?? "none",
|
|
295
|
-
prompt_hash: ctx.promptHash ?? "none",
|
|
296
|
-
};
|
|
297
|
-
if (ctx.repairedLine !== undefined)
|
|
298
|
-
trace.repaired_line = ctx.repairedLine;
|
|
299
|
-
if (ctx.conflictKind !== undefined)
|
|
300
|
-
trace.conflict_kind = ctx.conflictKind;
|
|
301
|
-
if (ctx.matchedExistingLine !== undefined)
|
|
302
|
-
trace.matched_existing_line = ctx.matchedExistingLine;
|
|
303
|
-
return trace;
|
|
304
|
-
}
|
|
305
|
-
function captureBaselineSnapshot(agentIds, projectRoot) {
|
|
306
|
-
const agents = agentIds.map((agentId) => {
|
|
307
|
-
const wp = resolveWritePaths(agentId, projectRoot);
|
|
308
|
-
let itemCount = 0;
|
|
309
|
-
let contentForHash = "";
|
|
310
|
-
for (const rp of wp.read_paths) {
|
|
311
|
-
if (fs.existsSync(rp)) {
|
|
312
|
-
const content = fs.readFileSync(rp, "utf8");
|
|
313
|
-
contentForHash += content;
|
|
314
|
-
const lines = content.split("\n");
|
|
315
|
-
itemCount += lines.filter((l) => /^[-*+]\s+\[/.test(l.trim())).length;
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
return {
|
|
319
|
-
agent_id: agentId,
|
|
320
|
-
file_paths: wp.read_paths,
|
|
321
|
-
item_count: itemCount,
|
|
322
|
-
content_hash: crypto
|
|
323
|
-
.createHash("sha256")
|
|
324
|
-
.update(contentForHash)
|
|
325
|
-
.digest("hex")
|
|
326
|
-
.slice(0, 16),
|
|
327
|
-
};
|
|
328
|
-
});
|
|
329
|
-
return { captured_at: isoTimestamp(), agents };
|
|
330
|
-
}
|
|
331
|
-
// ---------------------------------------------------------------------------
|
|
332
|
-
// Load existing learning lines for dedup
|
|
333
|
-
// ---------------------------------------------------------------------------
|
|
334
|
-
function loadExistingLines(readPaths) {
|
|
335
|
-
const lines = [];
|
|
336
|
-
for (const fp of readPaths) {
|
|
337
|
-
if (!fs.existsSync(fp))
|
|
338
|
-
continue;
|
|
339
|
-
const content = fs.readFileSync(fp, "utf8");
|
|
340
|
-
for (const line of content.split("\n")) {
|
|
341
|
-
const trimmed = line.trim();
|
|
342
|
-
if (/^[-*+]\s+\[/.test(trimmed)) {
|
|
343
|
-
lines.push(trimmed);
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
return lines;
|
|
348
|
-
}
|
|
349
|
-
// ---------------------------------------------------------------------------
|
|
350
|
-
// Main orchestrator
|
|
351
|
-
// ---------------------------------------------------------------------------
|
|
352
|
-
export async function runLearningExtraction(config) {
|
|
353
|
-
const timestamp = isoTimestamp();
|
|
354
|
-
const errors = [];
|
|
355
|
-
const itemTraces = [];
|
|
356
|
-
const conflictProposals = [];
|
|
357
|
-
const allMarkerTraces = [];
|
|
358
|
-
// Collect unique agent IDs
|
|
359
|
-
const agentIds = config.executionPlan.lens_prompt_packet_seats.map((s) => s.lens_id);
|
|
360
|
-
// Baseline snapshot (R4-IA3: captured at extractor entry)
|
|
361
|
-
try {
|
|
362
|
-
const baseline = captureBaselineSnapshot(agentIds, config.projectRoot);
|
|
363
|
-
const snapshotPath = path.join(config.sessionRoot, "execution-preparation", "learning-baseline-snapshot.yaml");
|
|
364
|
-
const snapshotDir = path.dirname(snapshotPath);
|
|
365
|
-
if (!fs.existsSync(snapshotDir)) {
|
|
366
|
-
fs.mkdirSync(snapshotDir, { recursive: true });
|
|
367
|
-
}
|
|
368
|
-
fs.writeFileSync(snapshotPath, JSON.stringify(baseline, null, 2), "utf8");
|
|
369
|
-
}
|
|
370
|
-
catch (error) {
|
|
371
|
-
errors.push(`Baseline snapshot failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
372
|
-
}
|
|
373
|
-
// Process each lens output
|
|
374
|
-
for (const seat of config.executionPlan.lens_prompt_packet_seats) {
|
|
375
|
-
const lensId = seat.lens_id;
|
|
376
|
-
const outputPath = seat.output_path;
|
|
377
|
-
try {
|
|
378
|
-
if (!fs.existsSync(outputPath))
|
|
379
|
-
continue;
|
|
380
|
-
const lensOutput = fs.readFileSync(outputPath, "utf8");
|
|
381
|
-
// Parse ### Newly Learned section
|
|
382
|
-
const rawLines = parseNewlyLearnedSection(lensOutput);
|
|
383
|
-
// Resolve paths for this agent
|
|
384
|
-
const writePaths = resolveWritePaths(lensId, config.projectRoot);
|
|
385
|
-
const existingLines = loadExistingLines(writePaths.read_paths);
|
|
386
|
-
// Process each raw learning line through the DAG
|
|
387
|
-
for (const rawLine of rawLines) {
|
|
388
|
-
try {
|
|
389
|
-
const result = await processItem(rawLine, lensId, config, existingLines, writePaths);
|
|
390
|
-
itemTraces.push(result.trace);
|
|
391
|
-
if (result.conflictProposal) {
|
|
392
|
-
conflictProposals.push(result.conflictProposal);
|
|
393
|
-
}
|
|
394
|
-
// If saved in active mode, add to existing lines for subsequent dedup
|
|
395
|
-
if (result.trace.kind === "classified" &&
|
|
396
|
-
result.trace.persistence_result === "written") {
|
|
397
|
-
existingLines.push(result.trace.assembled_line);
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
catch (error) {
|
|
401
|
-
errors.push(`[${lensId}] Item processing error: ${error instanceof Error ? error.message : String(error)}`);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
// C-11: Parse and process event markers
|
|
405
|
-
const markers = parseEventMarkers(lensOutput, lensId, config.sessionId, isoDate());
|
|
406
|
-
if (markers.length > 0) {
|
|
407
|
-
const allLearningPaths = resolveWritePaths(lensId, config.projectRoot).read_paths;
|
|
408
|
-
const markerTraces = matchMarkersToLearnings(markers, allLearningPaths);
|
|
409
|
-
// Attach markers (active only, shadow skips writes)
|
|
410
|
-
attachEventMarkers(markerTraces, config.mode);
|
|
411
|
-
allMarkerTraces.push(...markerTraces);
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
catch (error) {
|
|
415
|
-
errors.push(`[${lensId}] Lens processing error: ${error instanceof Error ? error.message : String(error)}`);
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
// Build classified traces early (needed by quarantine + manifest)
|
|
419
|
-
const classifiedTraces = itemTraces.filter((t) => t.kind === "classified");
|
|
420
|
-
// Quarantine file (for quarantined items + unclassified_pending — REC-4)
|
|
421
|
-
const quarantinedItems = itemTraces.filter((t) => t.kind === "quarantined");
|
|
422
|
-
const unclassifiedItems = classifiedTraces.filter((t) => t.decision === "unclassified_pending");
|
|
423
|
-
const allQuarantineItems = [...quarantinedItems, ...unclassifiedItems];
|
|
424
|
-
if (allQuarantineItems.length > 0) {
|
|
425
|
-
try {
|
|
426
|
-
const quarantinePath = path.join(config.sessionRoot, "execution-preparation", "learning-quarantine.yaml");
|
|
427
|
-
const quarantineDir = path.dirname(quarantinePath);
|
|
428
|
-
if (!fs.existsSync(quarantineDir)) {
|
|
429
|
-
fs.mkdirSync(quarantineDir, { recursive: true });
|
|
430
|
-
}
|
|
431
|
-
fs.writeFileSync(quarantinePath, JSON.stringify(allQuarantineItems, null, 2), "utf8");
|
|
432
|
-
}
|
|
433
|
-
catch (error) {
|
|
434
|
-
errors.push(`Quarantine write failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
// Build manifest
|
|
438
|
-
const manifest = {
|
|
439
|
-
schema_version: "1",
|
|
440
|
-
session_id: config.sessionId,
|
|
441
|
-
extract_mode: config.mode,
|
|
442
|
-
taxonomy_version: "phase2-v1",
|
|
443
|
-
timestamp,
|
|
444
|
-
items_parsed: itemTraces.length,
|
|
445
|
-
items_saved: classifiedTraces.filter((t) => t.persistence_result === "written").length,
|
|
446
|
-
items_quarantined: quarantinedItems.length,
|
|
447
|
-
items_duplicate_skipped: classifiedTraces.filter((t) => t.decision === "duplicate_skip").length,
|
|
448
|
-
items_conflict_proposed: conflictProposals.length,
|
|
449
|
-
items_unclassified_pending: classifiedTraces.filter((t) => t.decision === "unclassified_pending").length,
|
|
450
|
-
markers_found: allMarkerTraces.length,
|
|
451
|
-
markers_attached: allMarkerTraces.filter((t) => t.resolution === "attached").length,
|
|
452
|
-
markers_skipped_shadow: allMarkerTraces.filter((t) => t.resolution === "skipped_shadow").length,
|
|
453
|
-
markers_unresolved: allMarkerTraces.filter((t) => t.resolution === "unresolved_no_id" ||
|
|
454
|
-
t.resolution === "unresolved_not_found").length,
|
|
455
|
-
item_traces: itemTraces,
|
|
456
|
-
marker_traces: allMarkerTraces,
|
|
457
|
-
conflict_proposals: conflictProposals,
|
|
458
|
-
errors,
|
|
459
|
-
};
|
|
460
|
-
return manifest;
|
|
461
|
-
}
|
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Event marker processing — C-11 (Phase 2).
|
|
3
|
-
*
|
|
4
|
-
* Handles applied-then-found-invalid markers only.
|
|
5
|
-
* observed-obsolete → Phase 3 이연.
|
|
6
|
-
* CONS-5: learning_id 기반 대상 특정만. 레거시 항목(learning_id 없음) 부착 금지.
|
|
7
|
-
*/
|
|
8
|
-
import fs from "node:fs";
|
|
9
|
-
import { EVENT_MARKERS_HEADER_RE } from "./shared/patterns.js";
|
|
10
|
-
/** learning_id comment pattern: <!-- learning_id: {hash} [taxonomy_version: ...] --> */
|
|
11
|
-
const LEARNING_ID_RE = /<!--\s*learning_id:\s*(\w+)/;
|
|
12
|
-
/**
|
|
13
|
-
* Parse event markers from a lens output.
|
|
14
|
-
*/
|
|
15
|
-
export function parseEventMarkers(lensOutput, lensId, sessionId, date) {
|
|
16
|
-
const headerMatch = lensOutput.match(EVENT_MARKERS_HEADER_RE);
|
|
17
|
-
if (!headerMatch)
|
|
18
|
-
return [];
|
|
19
|
-
const sectionStart = headerMatch.index + headerMatch[0].length;
|
|
20
|
-
const rest = lensOutput.slice(sectionStart);
|
|
21
|
-
// Find where the section ends (next ## heading or end of string)
|
|
22
|
-
const nextSectionMatch = rest.match(/\n##\s/);
|
|
23
|
-
const sectionText = nextSectionMatch
|
|
24
|
-
? rest.slice(0, nextSectionMatch.index)
|
|
25
|
-
: rest;
|
|
26
|
-
const markers = [];
|
|
27
|
-
const lines = sectionText.split("\n");
|
|
28
|
-
let currentMarker = null;
|
|
29
|
-
for (const line of lines) {
|
|
30
|
-
const trimmed = line.trim();
|
|
31
|
-
if (!trimmed || trimmed.startsWith("##"))
|
|
32
|
-
continue;
|
|
33
|
-
const markerMatch = trimmed.match(/^-\s*marker:\s*(.+)$/);
|
|
34
|
-
if (markerMatch) {
|
|
35
|
-
if (currentMarker?.marker_type && currentMarker.learning_excerpt) {
|
|
36
|
-
markers.push(finalizeMarker(currentMarker, lensId, sessionId, date));
|
|
37
|
-
}
|
|
38
|
-
currentMarker =
|
|
39
|
-
markerMatch[1]?.trim() === "applied-then-found-invalid"
|
|
40
|
-
? { marker_type: "applied-then-found-invalid" }
|
|
41
|
-
: null;
|
|
42
|
-
continue;
|
|
43
|
-
}
|
|
44
|
-
if (!currentMarker)
|
|
45
|
-
continue;
|
|
46
|
-
const excerptMatch = trimmed.match(/^-\s*learning_excerpt:\s*(.+)$/);
|
|
47
|
-
if (excerptMatch) {
|
|
48
|
-
currentMarker.learning_excerpt = excerptMatch[1].trim();
|
|
49
|
-
continue;
|
|
50
|
-
}
|
|
51
|
-
const reasonMatch = trimmed.match(/^-\s*reason:\s*(.+)$/);
|
|
52
|
-
if (reasonMatch) {
|
|
53
|
-
currentMarker.reason = reasonMatch[1].trim();
|
|
54
|
-
continue;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
// Flush last marker
|
|
58
|
-
if (currentMarker?.marker_type && currentMarker.learning_excerpt) {
|
|
59
|
-
markers.push(finalizeMarker(currentMarker, lensId, sessionId, date));
|
|
60
|
-
}
|
|
61
|
-
return markers;
|
|
62
|
-
}
|
|
63
|
-
function finalizeMarker(partial, lensId, sessionId, date) {
|
|
64
|
-
return {
|
|
65
|
-
marker_type: "applied-then-found-invalid",
|
|
66
|
-
learning_id: null, // resolved later by matchMarkersToLearnings
|
|
67
|
-
learning_excerpt: partial.learning_excerpt ?? "",
|
|
68
|
-
reason: partial.reason ?? "",
|
|
69
|
-
lens_id: lensId,
|
|
70
|
-
session_id: sessionId,
|
|
71
|
-
date,
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Match markers to learning items by searching for excerpts in learning files.
|
|
76
|
-
* Only items with learning_id are eligible (CONS-5: 레거시 부착 금지).
|
|
77
|
-
*/
|
|
78
|
-
export function matchMarkersToLearnings(markers, learningFilePaths) {
|
|
79
|
-
// Load all learning lines with learning_ids
|
|
80
|
-
const learningIndex = new Map();
|
|
81
|
-
for (const filePath of learningFilePaths) {
|
|
82
|
-
if (!fs.existsSync(filePath))
|
|
83
|
-
continue;
|
|
84
|
-
const content = fs.readFileSync(filePath, "utf8");
|
|
85
|
-
const lines = content.split("\n");
|
|
86
|
-
for (let i = 0; i < lines.length; i++) {
|
|
87
|
-
const line = lines[i];
|
|
88
|
-
const idMatch = line.match(LEARNING_ID_RE);
|
|
89
|
-
if (idMatch?.[1]) {
|
|
90
|
-
// Index the content line BEFORE the learning_id comment, not the comment itself.
|
|
91
|
-
// Learning files store: content line \n <!-- learning_id: {hash} -->
|
|
92
|
-
const contentLine = i > 0 ? lines[i - 1] : line;
|
|
93
|
-
learningIndex.set(idMatch[1], {
|
|
94
|
-
learning_id: idMatch[1],
|
|
95
|
-
file_path: filePath,
|
|
96
|
-
line: contentLine,
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
const traces = [];
|
|
102
|
-
for (const marker of markers) {
|
|
103
|
-
// Search by excerpt substring match against lines with learning_id
|
|
104
|
-
let matched = false;
|
|
105
|
-
for (const [learningId, entry] of learningIndex) {
|
|
106
|
-
if (entry.line.includes(marker.learning_excerpt)) {
|
|
107
|
-
traces.push({
|
|
108
|
-
lens_id: marker.lens_id,
|
|
109
|
-
marker_type: "applied-then-found-invalid",
|
|
110
|
-
learning_excerpt: marker.learning_excerpt,
|
|
111
|
-
target_learning_id: learningId,
|
|
112
|
-
resolution: "attached",
|
|
113
|
-
target_file: entry.file_path,
|
|
114
|
-
});
|
|
115
|
-
marker.learning_id = learningId;
|
|
116
|
-
matched = true;
|
|
117
|
-
break;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
if (!matched) {
|
|
121
|
-
// Check if any line (without learning_id) matches → unresolved_no_id
|
|
122
|
-
let foundInLegacy = false;
|
|
123
|
-
for (const filePath of learningFilePaths) {
|
|
124
|
-
if (!fs.existsSync(filePath))
|
|
125
|
-
continue;
|
|
126
|
-
const content = fs.readFileSync(filePath, "utf8");
|
|
127
|
-
if (content.includes(marker.learning_excerpt)) {
|
|
128
|
-
foundInLegacy = true;
|
|
129
|
-
break;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
traces.push({
|
|
133
|
-
lens_id: marker.lens_id,
|
|
134
|
-
marker_type: "applied-then-found-invalid",
|
|
135
|
-
learning_excerpt: marker.learning_excerpt,
|
|
136
|
-
target_learning_id: null,
|
|
137
|
-
resolution: foundInLegacy ? "unresolved_no_id" : "unresolved_not_found",
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
return traces;
|
|
142
|
-
}
|
|
143
|
-
/**
|
|
144
|
-
* Attach event markers to learning files (active mode only).
|
|
145
|
-
* Format: <!-- applied-then-found-invalid: {date}, {session_id}, {reason}, target:{learning_id} -->
|
|
146
|
-
*/
|
|
147
|
-
export function attachEventMarkers(traces, mode) {
|
|
148
|
-
let attached = 0;
|
|
149
|
-
let skipped = 0;
|
|
150
|
-
for (const trace of traces) {
|
|
151
|
-
if (trace.resolution !== "attached" || !trace.target_learning_id) {
|
|
152
|
-
skipped++;
|
|
153
|
-
continue;
|
|
154
|
-
}
|
|
155
|
-
if (mode === "shadow") {
|
|
156
|
-
trace.resolution = "skipped_shadow";
|
|
157
|
-
skipped++;
|
|
158
|
-
continue;
|
|
159
|
-
}
|
|
160
|
-
if (!trace.target_file || !fs.existsSync(trace.target_file)) {
|
|
161
|
-
skipped++;
|
|
162
|
-
continue;
|
|
163
|
-
}
|
|
164
|
-
const markerComment = `<!-- applied-then-found-invalid: ${new Date().toISOString().slice(0, 10)}, ${trace.learning_excerpt.slice(0, 50)}, target:${trace.target_learning_id} -->`;
|
|
165
|
-
const content = fs.readFileSync(trace.target_file, "utf8");
|
|
166
|
-
const lines = content.split("\n");
|
|
167
|
-
const targetIdPattern = `<!-- learning_id: ${trace.target_learning_id} -->`;
|
|
168
|
-
const targetIdx = lines.findIndex((l) => l.includes(targetIdPattern));
|
|
169
|
-
if (targetIdx === -1) {
|
|
170
|
-
skipped++;
|
|
171
|
-
continue;
|
|
172
|
-
}
|
|
173
|
-
// Insert marker after the learning_id line
|
|
174
|
-
lines.splice(targetIdx + 1, 0, markerComment);
|
|
175
|
-
fs.writeFileSync(trace.target_file, lines.join("\n"), "utf8");
|
|
176
|
-
attached++;
|
|
177
|
-
}
|
|
178
|
-
return { attached, skipped };
|
|
179
|
-
}
|