heyspex 0.1.0 → 0.2.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/README.md +21 -0
- package/bin/hs.mjs +931 -98
- package/bin/spexcode.mjs +262 -0
- package/dev-factory/engine/worktree.mjs +740 -0
- package/dev-factory/lib/code-repo-root.mjs +67 -0
- package/dev-factory/lib/context-budget.mjs +84 -0
- package/dev-factory/lib/epic-detect.mjs +190 -0
- package/dev-factory/lib/fleet-queue-file.mjs +846 -0
- package/dev-factory/lib/fleet-queue.mjs +426 -0
- package/dev-factory/lib/fleet-state.mjs +14 -18
- package/dev-factory/lib/model-catalog.mjs +338 -0
- package/dev-factory/lib/seat-context.mjs +199 -0
- package/dev-factory/lib/spec-io.mjs +93 -0
- package/dev-factory/lib/sufficiency-score.mjs +8 -2
- package/dev-factory/lib/worktree-allocator.mjs +744 -0
- package/dev-factory/model-catalog.json +222 -0
- package/dist/cli/hs-check.mjs +1085 -103
- package/dist/cli/hs-claim.mjs +936 -63
- package/dist/cli/hs-connect.mjs +9786 -1910
- package/dist/cli/hs-defer.mjs +5267 -3053
- package/dist/cli/hs-dev.mjs +564 -0
- package/dist/cli/hs-env.mjs +385 -299
- package/dist/cli/hs-index-rebuild.mjs +7129 -0
- package/dist/cli/hs-index-reconcile.mjs +7852 -0
- package/dist/cli/hs-init.mjs +777 -138
- package/dist/cli/hs-install.mjs +6583 -485
- package/dist/cli/hs-join.mjs +10137 -2368
- package/dist/cli/hs-vault-pull.mjs +4659 -5882
- package/dist/cli/hs-vault.mjs +3793 -1537
- package/dist/cli/hs-workspace.mjs +2550 -524
- package/dist/cli-templates/customer-vault/specs/entity-model.md +594 -601
- package/dist/cli-templates/customer-vault/specs/protocols/canonical-frontmatter-templates.md +110 -177
- package/dist/cli-templates/customer-vault/specs/protocols/destructive-action-checklist.md +122 -0
- package/dist/cli-templates/customer-vault/specs/protocols/memory-protocol.md +13 -15
- package/dist/cli-templates/customer-vault/specs/protocols/session-close-protocol.md +91 -0
- package/dist/cli-templates/customer-vault/specs/protocols/spec-hierarchy.md +150 -168
- package/dist/cli-templates/customer-vault/specs/protocols/spec-summary-authoring.md +83 -0
- package/dist/cli-templates/harness-commands/claude/hs-auto.md +36 -0
- package/dist/cli-templates/harness-commands/claude/hs-backfill.md +28 -0
- package/dist/cli-templates/harness-commands/claude/hs-capture.md +62 -0
- package/dist/cli-templates/harness-commands/claude/hs-defer.md +29 -0
- package/dist/cli-templates/harness-commands/claude/hs-dev.md +20 -0
- package/dist/cli-templates/harness-commands/claude/hs-doctor.md +32 -0
- package/dist/cli-templates/harness-commands/claude/hs-ground.md +28 -0
- package/dist/cli-templates/harness-commands/claude/hs-handoff.md +33 -0
- package/dist/cli-templates/harness-commands/claude/hs-log.md +27 -0
- package/dist/cli-templates/harness-commands/claude/hs-next.md +29 -0
- package/dist/cli-templates/harness-commands/claude/hs-promote.md +70 -0
- package/dist/cli-templates/harness-commands/claude/hs-push.md +35 -0
- package/dist/cli-templates/harness-commands/claude/hs-qc.md +27 -0
- package/dist/cli-templates/harness-commands/claude/hs-res.md +63 -0
- package/dist/cli-templates/harness-commands/claude/hs-resolve.md +23 -0
- package/dist/cli-templates/harness-commands/claude/hs-review.md +23 -0
- package/dist/cli-templates/harness-commands/claude/hs-view.md +26 -0
- package/dist/cli-templates/harness-commands/claude/kill-ports.md +32 -0
- package/dist/cli-templates/harness-commands/claude/prototype.md +91 -0
- package/dist/cli-templates/harness-commands/cursor/hs-auto.md +40 -0
- package/dist/cli-templates/harness-commands/cursor/hs-backfill.md +31 -0
- package/dist/cli-templates/harness-commands/cursor/hs-capture.md +65 -0
- package/dist/cli-templates/harness-commands/cursor/hs-defer.md +32 -0
- package/dist/cli-templates/harness-commands/cursor/hs-dev.md +24 -0
- package/dist/cli-templates/harness-commands/cursor/hs-doctor.md +36 -0
- package/dist/cli-templates/harness-commands/cursor/hs-ground.md +31 -0
- package/dist/cli-templates/harness-commands/cursor/hs-handoff.md +37 -0
- package/dist/cli-templates/harness-commands/cursor/hs-log.md +31 -0
- package/dist/cli-templates/harness-commands/cursor/hs-next.md +32 -0
- package/dist/cli-templates/harness-commands/cursor/hs-promote.md +73 -0
- package/dist/cli-templates/harness-commands/cursor/hs-push.md +38 -0
- package/dist/cli-templates/harness-commands/cursor/hs-qc.md +30 -0
- package/dist/cli-templates/harness-commands/cursor/hs-res.md +67 -0
- package/dist/cli-templates/harness-commands/cursor/hs-resolve.md +26 -0
- package/dist/cli-templates/harness-commands/cursor/hs-review.md +26 -0
- package/dist/cli-templates/harness-commands/cursor/hs-view.md +30 -0
- package/dist/cli-templates/harness-commands/cursor/kill-ports.md +35 -0
- package/dist/cli-templates/harness-commands/cursor/prototype.md +95 -0
- package/docs/mcp-client-config.md +259 -6
- package/package.json +231 -124
- package/plugins/heyspex/.claude-plugin/plugin.json +12 -0
- package/plugins/heyspex/.cursor-plugin/plugin.json +15 -0
- package/plugins/heyspex/.mcp.json +12 -0
- package/plugins/heyspex/bin/launch-mcp.mjs +95 -0
- package/plugins/heyspex/bin/run-hook.mjs +62 -0
- package/plugins/heyspex/hooks/cursor-hooks.json +227 -0
- package/plugins/heyspex/hooks/hooks.json +361 -0
- package/plugins/heyspex/mcp.json +13 -0
- package/scripts/check-mcp-freshness.mjs +17 -9
- package/scripts/claude-hooks/_env-vault-events.mjs +68 -0
- package/scripts/claude-hooks/_guardrail-events.mjs +96 -0
- package/scripts/claude-hooks/_guardrail-loader.mjs +494 -0
- package/scripts/claude-hooks/_intent-classifier-router.mjs +410 -0
- package/scripts/claude-hooks/_intent-classifier-semantic.mjs +351 -0
- package/scripts/claude-hooks/_run.mjs +120 -0
- package/scripts/claude-hooks/_transcript-text.mjs +60 -0
- package/scripts/claude-hooks/_vault-retrieval.mjs +322 -0
- package/scripts/claude-hooks/agent-spawn-delegation-gate.mjs +133 -0
- package/scripts/claude-hooks/capture-workflow-gate.mjs +11 -0
- package/scripts/claude-hooks/check-auto-memory-budget.mjs +396 -0
- package/scripts/claude-hooks/check-claude-md-budget.mjs +327 -0
- package/scripts/claude-hooks/check-mcp-freshness-hook.mjs +14 -0
- package/scripts/claude-hooks/claim-before-code-gate.mjs +11 -0
- package/scripts/claude-hooks/claim-liveness-shared.mjs +575 -0
- package/scripts/claude-hooks/close-terminal-on-ship.mjs +64 -0
- package/scripts/claude-hooks/coo-denylist-gate.mjs +11 -0
- package/scripts/claude-hooks/coo-guard-check.mjs +11 -0
- package/scripts/claude-hooks/deep-research-cost-gate.mjs +11 -0
- package/scripts/claude-hooks/dev-server-task-reminder.mjs +15 -0
- package/scripts/claude-hooks/emit-claim-status.mjs +41 -0
- package/scripts/claude-hooks/enforce-session-sequence.mjs +11 -0
- package/scripts/claude-hooks/enforce-vault-naming.mjs +11 -0
- package/scripts/claude-hooks/env-vault-guard.mjs +11 -0
- package/scripts/claude-hooks/epic-milestone-phrasing-gate.mjs +11 -0
- package/scripts/claude-hooks/git-identity-guard.mjs +11 -0
- package/scripts/claude-hooks/git-reflog-audit.mjs +15 -0
- package/scripts/claude-hooks/git-stash-gate.mjs +11 -0
- package/scripts/claude-hooks/guard-auto-memory-writes.mjs +137 -0
- package/scripts/claude-hooks/id-display-format-gate.mjs +11 -0
- package/scripts/claude-hooks/inject-communication-style.mjs +66 -0
- package/scripts/claude-hooks/inject-coo-seat.mjs +78 -0
- package/scripts/claude-hooks/inject-dev-server-awareness.mjs +11 -0
- package/scripts/claude-hooks/inject-doctrine.mjs +13 -0
- package/scripts/claude-hooks/inject-vault-context.mjs +14 -0
- package/scripts/claude-hooks/intent-classifier-trigger.mjs +247 -0
- package/scripts/claude-hooks/log-session-activity.mjs +341 -0
- package/scripts/claude-hooks/naughty-list-gate.mjs +11 -0
- package/scripts/claude-hooks/notify-status-flip.mjs +72 -0
- package/scripts/claude-hooks/plan-exit-ingest-gate.mjs +170 -0
- package/scripts/claude-hooks/plan-ingest-close-reminder.mjs +66 -0
- package/scripts/claude-hooks/plan-review-gate.mjs +11 -0
- package/scripts/claude-hooks/preflight-reminder.mjs +262 -0
- package/scripts/claude-hooks/protected-action-intent.mjs +221 -0
- package/scripts/claude-hooks/research-method-guard.mjs +398 -0
- package/scripts/claude-hooks/research-protocol-gate.mjs +11 -0
- package/scripts/claude-hooks/session-end.mjs +449 -0
- package/scripts/claude-hooks/settings-write-gate.mjs +11 -0
- package/scripts/claude-hooks/settlement-check-gate.mjs +39 -0
- package/scripts/claude-hooks/status-flip-detect.mjs +17 -0
- package/scripts/claude-hooks/terminal-close-shared.mjs +426 -0
- package/scripts/claude-hooks/terminal-close-watcher.mjs +122 -0
- package/scripts/claude-hooks/wip-checkpoint.mjs +298 -0
- package/scripts/cursor-hooks/manifest.mjs +235 -0
- package/scripts/dev-server-status.mjs +34 -13
- package/scripts/dev-server-stop.mjs +313 -0
- package/scripts/heyspex-mode.mjs +268 -0
- package/scripts/hs-auto-resolve.mjs +240 -0
- package/scripts/hs-auto-source.mjs +398 -0
- package/scripts/hs-auto.mjs +134 -0
- package/scripts/hs-capture-cli.mjs +326 -0
- package/scripts/hs-classifier.mjs +136 -0
- package/scripts/hs-companion.mjs +88 -0
- package/scripts/hs-gateway.mjs +115 -0
- package/scripts/hs-mode.mjs +125 -0
- package/scripts/hs-next.mjs +441 -0
- package/scripts/hs-settings.mjs +338 -0
- package/scripts/hs-view.mjs +333 -0
- package/scripts/hs-worktree.mjs +201 -0
- package/scripts/install-claude-code-hooks.mjs +270 -0
- package/scripts/install-cursor-hooks.mjs +471 -0
- package/scripts/lib/agent-env-markers.mjs +38 -0
- package/scripts/lib/console-url-resolver.mjs +197 -60
- package/scripts/lib/customer-kit.mjs +886 -0
- package/scripts/lib/dev-mode.mjs +244 -28
- package/scripts/lib/heyspex-home.mjs +72 -0
- package/scripts/lib/hs-command.mjs +277 -0
- package/scripts/lib/hs-companion-settings.mjs +472 -0
- package/scripts/lib/hs-source-commit.mjs +59 -5
- package/scripts/lib/hs-source-identity.mjs +526 -0
- package/scripts/lib/hs-source-log.mjs +11 -21
- package/scripts/lib/hs-source-push.mjs +107 -58
- package/scripts/lib/hs-worktree-args.mjs +115 -0
- package/scripts/lib/shared-path-helpers.mjs +134 -0
- package/scripts/lib/standalone-doctor.mjs +540 -3
- package/scripts/lib/subagent-git-lock.mjs +109 -0
- package/scripts/lib/template-pin.mjs +34 -0
- package/scripts/provenance-lib.mjs +91 -0
- package/scripts/stamp-provenance-on-edit.mjs +222 -0
- package/scripts/wip-ledger-health.mjs +653 -0
- package/spexharness/doctrine/baseline/SPEXCODE-DOCTRINE.baseline.json +29 -0
- package/spexharness/src/adapters/audit.mjs +197 -0
- package/spexharness/src/adapters/claude-code.mjs +441 -0
- package/spexharness/src/chat/tui/ansi.mjs +448 -0
- package/spexharness/src/chat/tui/palette.mjs +22 -0
- package/spexharness/src/chat/tui/select.mjs +208 -0
- package/spexharness/src/config-root.mjs +54 -0
- package/spexharness/src/context-budget.mjs +141 -0
- package/spexharness/src/dev-server-control.mjs +389 -0
- package/spexharness/src/doctrine/behaviour.mjs +371 -0
- package/spexharness/src/doctrine/corpus.mjs +733 -0
- package/spexharness/src/doctrine/inject.mjs +338 -0
- package/spexharness/src/doctrine/merge.mjs +184 -0
- package/spexharness/src/doctrine/sources.mjs +252 -0
- package/spexharness/src/doctrine/synthesize.mjs +401 -0
- package/spexharness/src/doctrine/vault-source.mjs +574 -0
- package/spexharness/src/doctrine/writer.mjs +176 -0
- package/spexharness/src/events.mjs +84 -0
- package/spexharness/src/gates/capture-workflow-gate.mjs +202 -0
- package/spexharness/src/gates/claim-before-code-gate.decide.mjs +33 -0
- package/spexharness/src/gates/claim-before-code-gate.mjs +129 -0
- package/spexharness/src/gates/deep-research-cost-gate.mjs +274 -0
- package/spexharness/src/gates/enforce-session-sequence.mjs +895 -0
- package/spexharness/src/gates/enforce-vault-naming.mjs +215 -0
- package/spexharness/src/gates/epic-milestone-phrasing-gate.mjs +233 -0
- package/spexharness/src/gates/git-identity-guard.decide.mjs +436 -0
- package/spexharness/src/gates/git-identity-guard.mjs +61 -0
- package/spexharness/src/gates/git-reflog-audit.mjs +151 -0
- package/spexharness/src/gates/git-stash-gate.decide.mjs +361 -0
- package/spexharness/src/gates/git-stash-gate.mjs +159 -0
- package/spexharness/src/gates/id-display-format-gate.mjs +190 -0
- package/spexharness/src/gates/inject-doctrine-gate.mjs +89 -0
- package/spexharness/src/gates/inject-vault-context.mjs +239 -0
- package/spexharness/src/gates/naughty-list-gate.mjs +213 -0
- package/spexharness/src/gates/plan-review-gate.mjs +233 -0
- package/spexharness/src/gates/preflight-reminder-gate.mjs +265 -0
- package/spexharness/src/gates/research-protocol-gate.mjs +175 -0
- package/spexharness/src/gates/settings-write-gate.decide.mjs +96 -0
- package/spexharness/src/gates/settings-write-gate.mjs +104 -0
- package/spexharness/src/git-reflog-audit.mjs +294 -0
- package/spexharness/src/intent-classifier.mjs +194 -0
- package/spexharness/src/intent-directive.mjs +541 -0
- package/spexharness/src/profile/learn.mjs +551 -0
- package/spexharness/src/profile/proposals.mjs +357 -0
- package/spexharness/src/profile/vault-inclusion.mjs +386 -0
- package/spexharness/src/redact.mjs +151 -0
- package/spexharness/src/rule-table.mjs +819 -0
- package/spexharness/src/seat-context.mjs +122 -0
- package/spexharness/src/session-bootstrap.mjs +512 -0
- package/spexharness/src/session-record.mjs +815 -0
- package/spexharness/src/token-inventory.mjs +207 -0
- package/vendor/heyspex-mcp/account-switch.mjs +95 -0
- package/vendor/heyspex-mcp/alignment-gate.mjs +315 -1
- package/vendor/heyspex-mcp/blocker-resolver.mjs +37 -2
- package/vendor/heyspex-mcp/breakdown-ladder.mjs +119 -4
- package/vendor/heyspex-mcp/breakdown-source.mjs +271 -0
- package/vendor/heyspex-mcp/breakdown-writer.mjs +23 -3
- package/vendor/heyspex-mcp/capture-buffer-patches.mjs +46 -5
- package/vendor/heyspex-mcp/capture-channel.mjs +55 -5
- package/vendor/heyspex-mcp/capture-console-client.mjs +80 -1
- package/vendor/heyspex-mcp/capture-focus.mjs +1035 -29
- package/vendor/heyspex-mcp/capture-outbox.mjs +5 -1
- package/vendor/heyspex-mcp/capture-tools.mjs +1219 -0
- package/vendor/heyspex-mcp/claim-tools.mjs +1124 -0
- package/vendor/heyspex-mcp/cli-verbs.mjs +2076 -0
- package/vendor/heyspex-mcp/cli.mjs +201 -15524
- package/vendor/heyspex-mcp/cloud-tool-surface.mjs +128 -0
- package/vendor/heyspex-mcp/cloud-vault-writes.mjs +412 -0
- package/vendor/heyspex-mcp/config-seed.yaml +15 -0
- package/vendor/heyspex-mcp/config-settings.mjs +290 -7
- package/vendor/heyspex-mcp/console-fetch.mjs +657 -16
- package/vendor/heyspex-mcp/console-transport.mjs +166 -0
- package/vendor/heyspex-mcp/credentials.mjs +584 -0
- package/vendor/heyspex-mcp/defer-spec.mjs +44 -4
- package/vendor/heyspex-mcp/deferred-reclaim.mjs +29 -3
- package/vendor/heyspex-mcp/design-handlers.mjs +31 -0
- package/vendor/heyspex-mcp/design-tools.mjs +4 -1
- package/vendor/heyspex-mcp/dev-factory-bridge.mjs +4 -4
- package/vendor/heyspex-mcp/dev-mode-core.mjs +45 -0
- package/vendor/heyspex-mcp/dev-tools.mjs +124 -0
- package/vendor/heyspex-mcp/diagnose-handlers.mjs +54 -0
- package/vendor/heyspex-mcp/events-tools.mjs +207 -0
- package/vendor/heyspex-mcp/filing-tools.mjs +688 -0
- package/vendor/heyspex-mcp/fleet-handlers.mjs +37 -0
- package/vendor/heyspex-mcp/fleet-queue-tools.mjs +69 -14
- package/vendor/heyspex-mcp/focus-tools.mjs +190 -0
- package/vendor/heyspex-mcp/folder-trust-store.mjs +59 -0
- package/vendor/heyspex-mcp/http-transport.mjs +300 -0
- package/vendor/heyspex-mcp/list-tools.mjs +257 -0
- package/vendor/heyspex-mcp/login-handoff.mjs +15 -1
- package/vendor/heyspex-mcp/mcp-config.mjs +94 -0
- package/vendor/heyspex-mcp/memory-tools.mjs +523 -0
- package/vendor/heyspex-mcp/mvp-mode.mjs +53 -6
- package/vendor/heyspex-mcp/occupied-keys.mjs +141 -0
- package/vendor/heyspex-mcp/on-deck-brief.mjs +3360 -0
- package/vendor/heyspex-mcp/on-deck-scheduler.mjs +1708 -0
- package/vendor/heyspex-mcp/package-root.mjs +32 -0
- package/vendor/heyspex-mcp/peer-presence.mjs +8 -1
- package/vendor/heyspex-mcp/plan-ground.mjs +621 -652
- package/vendor/heyspex-mcp/plan-push.mjs +906 -21
- package/vendor/heyspex-mcp/plan-review.mjs +124 -30
- package/vendor/heyspex-mcp/preflight-gate.mjs +0 -0
- package/vendor/heyspex-mcp/procedure-handlers.mjs +325 -0
- package/vendor/heyspex-mcp/procedure-tools.mjs +540 -351
- package/vendor/heyspex-mcp/promote-capture.mjs +386 -69
- package/vendor/heyspex-mcp/promote-diagnose.mjs +74 -0
- package/vendor/heyspex-mcp/promote-research-to-diagnose.mjs +9 -2
- package/vendor/heyspex-mcp/promote-research.mjs +660 -53
- package/vendor/heyspex-mcp/promotion-helpers.mjs +251 -0
- package/vendor/heyspex-mcp/prompts.mjs +1460 -0
- package/vendor/heyspex-mcp/read-path.mjs +29 -9
- package/vendor/heyspex-mcp/receipt-shape.mjs +146 -0
- package/vendor/heyspex-mcp/recipe-handlers.mjs +117 -0
- package/vendor/heyspex-mcp/regen-tasks.mjs +210 -3
- package/vendor/heyspex-mcp/register-spec.mjs +266 -54
- package/vendor/heyspex-mcp/request-context.mjs +59 -0
- package/vendor/heyspex-mcp/research-focus.mjs +207 -4
- package/vendor/heyspex-mcp/research-handlers.mjs +623 -0
- package/vendor/heyspex-mcp/research-tools.mjs +4 -4
- package/vendor/heyspex-mcp/response-budget.mjs +348 -0
- package/vendor/heyspex-mcp/review-focus.mjs +25 -1
- package/vendor/heyspex-mcp/search-specs.mjs +280 -0
- package/vendor/heyspex-mcp/seat-block-ledger.mjs +113 -0
- package/vendor/heyspex-mcp/seat-identity.mjs +2 -6
- package/vendor/heyspex-mcp/session-owner.mjs +108 -0
- package/vendor/heyspex-mcp/session-tools.mjs +152 -0
- package/vendor/heyspex-mcp/settings-registry.mjs +232 -0
- package/vendor/heyspex-mcp/settings-tools.mjs +85 -12
- package/vendor/heyspex-mcp/spec-edit-tools.mjs +446 -0
- package/vendor/heyspex-mcp/spec-id-lib.mjs +93 -17
- package/vendor/heyspex-mcp/split-research.mjs +301 -56
- package/vendor/heyspex-mcp/stdio-rpc.mjs +22 -0
- package/vendor/heyspex-mcp/submit-for-review.mjs +132 -6
- package/vendor/heyspex-mcp/summary-contract.mjs +105 -0
- package/vendor/heyspex-mcp/summary-write-tools.mjs +224 -0
- package/vendor/heyspex-mcp/symptom-query.mjs +146 -0
- package/vendor/heyspex-mcp/test-helpers.mjs +108 -1
- package/vendor/heyspex-mcp/tool-catalog.mjs +2813 -0
- package/vendor/heyspex-mcp/tool-dispatch.mjs +639 -0
- package/vendor/heyspex-mcp/tool-helpers.mjs +325 -0
- package/vendor/heyspex-mcp/validate-linkage.mjs +414 -27
- package/vendor/heyspex-mcp/vault-io.mjs +724 -0
- package/vendor/heyspex-mcp/vault-less-endpoints.mjs +52 -0
- package/vendor/heyspex-mcp/vault-resolve.mjs +366 -68
- package/vendor/heyspex-mcp/verify-reproduction.mjs +270 -30
- package/vendor/heyspex-mcp/whoami.mjs +91 -0
|
@@ -12,23 +12,25 @@
|
|
|
12
12
|
* heyspex_design_* family is the canonical template this module mirrors).
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
import { existsSync, readFileSync } from
|
|
16
|
-
import { join } from
|
|
15
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
16
|
+
import { dirname, join, resolve } from "node:path";
|
|
17
|
+
import { fileURLToPath } from "node:url";
|
|
17
18
|
|
|
18
|
-
import { readViaSelector } from
|
|
19
|
+
import { readViaSelector } from "./read-path.mjs";
|
|
20
|
+
import { pageListToolResult } from "./list-tools.mjs";
|
|
19
21
|
|
|
20
|
-
const CATALOG_FILE = [
|
|
21
|
-
const PROTOCOLS_DIR = [
|
|
22
|
+
const CATALOG_FILE = ["specs", "protocols", ".catalog.json"];
|
|
23
|
+
const PROTOCOLS_DIR = ["specs", "protocols"];
|
|
22
24
|
|
|
23
25
|
function normalize(value) {
|
|
24
|
-
return String(value ??
|
|
26
|
+
return String(value ?? "").toLowerCase();
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
export function loadCatalog(vaultRoot) {
|
|
28
30
|
const path = join(vaultRoot, ...CATALOG_FILE);
|
|
29
31
|
if (!existsSync(path)) return null;
|
|
30
32
|
try {
|
|
31
|
-
return JSON.parse(readFileSync(path,
|
|
33
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
32
34
|
} catch {
|
|
33
35
|
return null;
|
|
34
36
|
}
|
|
@@ -41,7 +43,7 @@ function summarize(entry) {
|
|
|
41
43
|
title: entry.title,
|
|
42
44
|
tags: Array.isArray(entry.tags) ? entry.tags : [],
|
|
43
45
|
triggers: Array.isArray(entry.triggers) ? entry.triggers : [],
|
|
44
|
-
summary: entry.summary ??
|
|
46
|
+
summary: entry.summary ?? "",
|
|
45
47
|
path: entry.path,
|
|
46
48
|
};
|
|
47
49
|
}
|
|
@@ -54,15 +56,18 @@ function summarize(entry) {
|
|
|
54
56
|
* preserves today's disk-only behavior byte-for-byte.
|
|
55
57
|
*/
|
|
56
58
|
export function executeListProcedures(vaultRoot, args = {}, injectedCatalog) {
|
|
57
|
-
const catalog =
|
|
59
|
+
const catalog =
|
|
60
|
+
injectedCatalog !== undefined ? injectedCatalog : loadCatalog(vaultRoot);
|
|
58
61
|
if (!catalog) {
|
|
59
62
|
return {
|
|
60
|
-
error:
|
|
63
|
+
error: "CATALOG_NOT_FOUND",
|
|
61
64
|
message:
|
|
62
|
-
|
|
65
|
+
"specs/protocols/.catalog.json not found. Run `node scripts/build-procedure-catalog.mjs`.",
|
|
63
66
|
};
|
|
64
67
|
}
|
|
65
|
-
const procedures = Array.isArray(catalog.procedures)
|
|
68
|
+
const procedures = Array.isArray(catalog.procedures)
|
|
69
|
+
? catalog.procedures
|
|
70
|
+
: [];
|
|
66
71
|
const tagFilter = args.tag ? normalize(args.tag) : null;
|
|
67
72
|
const triggerFilter = args.trigger ? normalize(args.trigger) : null;
|
|
68
73
|
const queryFilter = args.query ? normalize(args.query) : null;
|
|
@@ -80,59 +85,68 @@ export function executeListProcedures(vaultRoot, args = {}, injectedCatalog) {
|
|
|
80
85
|
if (queryFilter) {
|
|
81
86
|
const haystack = [entry.name, entry.title, entry.summary]
|
|
82
87
|
.map(normalize)
|
|
83
|
-
.join(
|
|
88
|
+
.join(" ");
|
|
84
89
|
if (!haystack.includes(queryFilter)) continue;
|
|
85
90
|
}
|
|
86
91
|
matches.push(summarize(entry));
|
|
87
92
|
}
|
|
88
93
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
94
|
+
// One page on a (name, id) keyset cursor, fitted to the byte budget of the
|
|
95
|
+
// tool — never the whole catalog, never a total count
|
|
96
|
+
// ([[HS-E66W-8KK5-TASK-mcp-list-tools-page-with-a-cursor-and-a]]).
|
|
97
|
+
const paged = pageListToolResult("heyspex_list_procedures", matches, args, {
|
|
98
|
+
keyOf: (p) => p.name,
|
|
99
|
+
idOf: (p) => p.id ?? p.name,
|
|
100
|
+
extra: {
|
|
101
|
+
filters_applied: {
|
|
102
|
+
tag: args.tag ?? null,
|
|
103
|
+
trigger: args.trigger ?? null,
|
|
104
|
+
query: args.query ?? null,
|
|
105
|
+
},
|
|
99
106
|
},
|
|
100
|
-
};
|
|
107
|
+
});
|
|
108
|
+
if (paged.error) return paged.error;
|
|
109
|
+
return paged.result;
|
|
101
110
|
}
|
|
102
111
|
|
|
103
112
|
function findEntry(catalog, name) {
|
|
104
|
-
const procedures = Array.isArray(catalog.procedures)
|
|
113
|
+
const procedures = Array.isArray(catalog.procedures)
|
|
114
|
+
? catalog.procedures
|
|
115
|
+
: [];
|
|
105
116
|
return procedures.find((p) => p.name === name) || null;
|
|
106
117
|
}
|
|
107
118
|
|
|
108
119
|
export function executeGetProcedure(vaultRoot, args = {}) {
|
|
109
|
-
const name = String(args.name ??
|
|
120
|
+
const name = String(args.name ?? "").trim();
|
|
110
121
|
if (!name) {
|
|
111
|
-
return {
|
|
122
|
+
return {
|
|
123
|
+
error: "MISSING_NAME",
|
|
124
|
+
message: 'name is required (e.g. "spec-hierarchy")',
|
|
125
|
+
};
|
|
112
126
|
}
|
|
113
127
|
const catalog = loadCatalog(vaultRoot);
|
|
114
128
|
if (!catalog) {
|
|
115
129
|
return {
|
|
116
|
-
error:
|
|
130
|
+
error: "CATALOG_NOT_FOUND",
|
|
117
131
|
message:
|
|
118
|
-
|
|
132
|
+
"specs/protocols/.catalog.json not found. Run `node scripts/build-procedure-catalog.mjs`.",
|
|
119
133
|
};
|
|
120
134
|
}
|
|
121
135
|
const entry = findEntry(catalog, name);
|
|
122
136
|
if (!entry) {
|
|
123
137
|
return {
|
|
124
|
-
error:
|
|
138
|
+
error: "PROCEDURE_NOT_FOUND",
|
|
125
139
|
message: `No procedure named "${name}" in catalog. Call heyspex_list_procedures to browse.`,
|
|
126
140
|
};
|
|
127
141
|
}
|
|
128
142
|
const bodyPath = join(vaultRoot, ...PROTOCOLS_DIR, `${name}.md`);
|
|
129
143
|
if (!existsSync(bodyPath)) {
|
|
130
144
|
return {
|
|
131
|
-
error:
|
|
145
|
+
error: "BODY_FILE_MISSING",
|
|
132
146
|
message: `Catalog entry exists but ${entry.path} not found on disk. Regenerate the catalog.`,
|
|
133
147
|
};
|
|
134
148
|
}
|
|
135
|
-
const content = readFileSync(bodyPath,
|
|
149
|
+
const content = readFileSync(bodyPath, "utf8");
|
|
136
150
|
return {
|
|
137
151
|
procedure: {
|
|
138
152
|
name: entry.name,
|
|
@@ -140,7 +154,7 @@ export function executeGetProcedure(vaultRoot, args = {}) {
|
|
|
140
154
|
title: entry.title,
|
|
141
155
|
tags: Array.isArray(entry.tags) ? entry.tags : [],
|
|
142
156
|
triggers: Array.isArray(entry.triggers) ? entry.triggers : [],
|
|
143
|
-
summary: entry.summary ??
|
|
157
|
+
summary: entry.summary ?? "",
|
|
144
158
|
path: entry.path,
|
|
145
159
|
status: entry.status ?? null,
|
|
146
160
|
created: entry.created ?? null,
|
|
@@ -152,395 +166,471 @@ export function executeGetProcedure(vaultRoot, args = {}) {
|
|
|
152
166
|
};
|
|
153
167
|
}
|
|
154
168
|
|
|
169
|
+
/**
|
|
170
|
+
* Procedures every customer vault ships (the universal template protocols; a
|
|
171
|
+
* drift test pins this list to the template manifest's protocol dests). Every
|
|
172
|
+
* other PREFLIGHT_INTENT_MAP procedure is platform-only: a vault whose runtime
|
|
173
|
+
* catalog lacks it gets the row DEMOTED to advisory by {@link resolveRow}, never
|
|
174
|
+
* deleted. Promoting more procedures to universal is a later template version.
|
|
175
|
+
*/
|
|
176
|
+
export const UNIVERSAL_PROCEDURES = Object.freeze([
|
|
177
|
+
"canonical-frontmatter-templates",
|
|
178
|
+
"spec-hierarchy",
|
|
179
|
+
"memory-protocol",
|
|
180
|
+
"session-close-protocol",
|
|
181
|
+
"spec-summary-authoring",
|
|
182
|
+
"destructive-action-checklist",
|
|
183
|
+
]);
|
|
184
|
+
|
|
185
|
+
/** Audience of a preflight row, by procedure name. */
|
|
186
|
+
export function procedureAudience(name) {
|
|
187
|
+
return UNIVERSAL_PROCEDURES.includes(name) ? "universal" : "platform";
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const DEMOTED_REASON = "not in this vault's catalog (platform-only procedure)";
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Resolve one preflight row against the supplied (runtime) catalog. Shared by
|
|
194
|
+
* the static intent path AND the close_session thread-type path. A row whose
|
|
195
|
+
* procedure is present is returned exactly as before; an absent one is demoted
|
|
196
|
+
* to `advisory` (original severity kept in `demoted_from`) with
|
|
197
|
+
* `missing_from_catalog: true` still set for consumers.
|
|
198
|
+
*/
|
|
199
|
+
export function resolveRow(catalog, row) {
|
|
200
|
+
const entry = findEntry(catalog, row.procedure);
|
|
201
|
+
const base = {
|
|
202
|
+
procedure: row.procedure,
|
|
203
|
+
...(row.why !== undefined ? { why: row.why } : {}),
|
|
204
|
+
severity: row.severity,
|
|
205
|
+
path: entry?.path ?? null,
|
|
206
|
+
title: entry?.title ?? null,
|
|
207
|
+
missing_from_catalog: entry ? undefined : true,
|
|
208
|
+
};
|
|
209
|
+
if (entry) return base;
|
|
210
|
+
return {
|
|
211
|
+
...base,
|
|
212
|
+
severity: "advisory",
|
|
213
|
+
demoted_from: row.severity,
|
|
214
|
+
reason: DEMOTED_REASON,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
155
218
|
// Bootstrap intent → procedure map (eight intents per HS-EPIC-3QS8 §Open
|
|
156
219
|
// questions "Trigger taxonomy"). Each row names the procedure slug (matches
|
|
157
220
|
// specs/protocols/<slug>.md), a `why` blurb the agent can quote, and a
|
|
158
221
|
// severity. Extension rule: add a new intent when a third unique use case
|
|
159
222
|
// appears — new intents land in their own tasks, not edits to this map.
|
|
160
223
|
const PREFLIGHT_INTENT_MAP = {
|
|
224
|
+
// [[HS-K0ZP]]: "about to do research" is a governed trigger. Running this
|
|
225
|
+
// preflight writes the proof-of-use marker that unlocks WebSearch / WebFetch /
|
|
226
|
+
// heyspex_save_research for the session (spexharness/src/gates/research-protocol-gate.mjs
|
|
227
|
+
// on every PreToolUse front, preflight-gate.mjs inside the MCP server). Before
|
|
228
|
+
// it, those calls are refused -- the protocol was prose an agent could skip.
|
|
229
|
+
research: [
|
|
230
|
+
{
|
|
231
|
+
procedure: "research-capture-protocol",
|
|
232
|
+
why: "Method triage FIRST (known-target-retrieval vs the confidence ladder), ground-first (heyspex_recall_memories + /hs-ground) before any external pass, and the offer-before-save step: a web search offers to save findings, it does not auto-file a research doc.",
|
|
233
|
+
severity: "required",
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
procedure: "capture-guardrails",
|
|
237
|
+
why: "A known-target lookup never enters the research-doc pipeline; the exit for a quick retrieval is a memory-reference or a fold into the consuming spec.",
|
|
238
|
+
severity: "recommended",
|
|
239
|
+
},
|
|
240
|
+
],
|
|
161
241
|
promote_research: [
|
|
162
242
|
{
|
|
163
|
-
procedure:
|
|
164
|
-
why:
|
|
165
|
-
severity:
|
|
243
|
+
procedure: "research-capture-protocol",
|
|
244
|
+
why: "Confirm the research doc is complete (prerequisites, conclusion, sources) before promotion.",
|
|
245
|
+
severity: "required",
|
|
166
246
|
},
|
|
167
247
|
{
|
|
168
|
-
procedure:
|
|
169
|
-
why:
|
|
170
|
-
severity:
|
|
248
|
+
procedure: "spec-hierarchy",
|
|
249
|
+
why: "Mandatory default: target_milestone NEW. Existing-milestone attach only when status: open and thesis clearly extends; in_progress requires explicit owner override.",
|
|
250
|
+
severity: "required",
|
|
171
251
|
},
|
|
172
252
|
{
|
|
173
|
-
procedure:
|
|
174
|
-
why:
|
|
175
|
-
severity:
|
|
253
|
+
procedure: "pillar-assignment-protocol",
|
|
254
|
+
why: "Every spawned epic must name a pillar before execution breakdown.",
|
|
255
|
+
severity: "recommended",
|
|
176
256
|
},
|
|
177
257
|
],
|
|
178
258
|
file_iteration: [
|
|
179
259
|
{
|
|
180
|
-
procedure:
|
|
181
|
-
why:
|
|
182
|
-
severity:
|
|
260
|
+
procedure: "supersession-protocol",
|
|
261
|
+
why: "§Iterates-on: choose the right tier; a re-attempt is neither supersession nor amends.",
|
|
262
|
+
severity: "required",
|
|
183
263
|
},
|
|
184
264
|
{
|
|
185
|
-
procedure:
|
|
265
|
+
procedure: "research-capture-protocol",
|
|
186
266
|
why: 'Promote flow asks "is this a re-attempt?" and populates iterates_on: — confirm iteration lineage is recorded.',
|
|
187
|
-
severity:
|
|
267
|
+
severity: "required",
|
|
188
268
|
},
|
|
189
269
|
{
|
|
190
|
-
procedure:
|
|
191
|
-
why:
|
|
192
|
-
severity:
|
|
270
|
+
procedure: "internal-linking-protocol",
|
|
271
|
+
why: "Iteration structure and tier-based lineage tracking; see entity-model §Iteration lineage for tier semantics.",
|
|
272
|
+
severity: "recommended",
|
|
193
273
|
},
|
|
194
274
|
],
|
|
195
275
|
file_milestone: [
|
|
196
276
|
{
|
|
197
|
-
procedure:
|
|
198
|
-
why:
|
|
199
|
-
severity:
|
|
277
|
+
procedure: "spec-hierarchy",
|
|
278
|
+
why: "Defines which level a new spec belongs at and the milestone frontmatter shape.",
|
|
279
|
+
severity: "required",
|
|
200
280
|
},
|
|
201
281
|
{
|
|
202
|
-
procedure:
|
|
203
|
-
why:
|
|
204
|
-
severity:
|
|
282
|
+
procedure: "spec-summary-authoring",
|
|
283
|
+
why: "Every milestone needs a plain-language summary: at filing time — pass summary to heyspex_file_spec / POST /api/specs.",
|
|
284
|
+
severity: "required",
|
|
205
285
|
},
|
|
206
286
|
{
|
|
207
|
-
procedure:
|
|
208
|
-
why:
|
|
209
|
-
severity:
|
|
287
|
+
procedure: "vault-id-assignment-protocol",
|
|
288
|
+
why: "Allocates a unique HS-MIL-<suffix> id and prevents duplicates.",
|
|
289
|
+
severity: "required",
|
|
210
290
|
},
|
|
211
291
|
],
|
|
212
292
|
file_epic: [
|
|
213
293
|
{
|
|
214
|
-
procedure:
|
|
215
|
-
why:
|
|
216
|
-
severity:
|
|
294
|
+
procedure: "spec-hierarchy",
|
|
295
|
+
why: "Defines epic frontmatter, required parent-milestone link, and child layout.",
|
|
296
|
+
severity: "required",
|
|
217
297
|
},
|
|
218
298
|
{
|
|
219
|
-
procedure:
|
|
220
|
-
why:
|
|
221
|
-
severity:
|
|
299
|
+
procedure: "spec-summary-authoring",
|
|
300
|
+
why: "Every epic needs a plain-language summary at plan-ingest filing time — pass summary to heyspex_push_plan / heyspex_file_spec / POST /api/specs. Do not file tasks in the same pass.",
|
|
301
|
+
severity: "required",
|
|
222
302
|
},
|
|
223
303
|
{
|
|
224
|
-
procedure:
|
|
225
|
-
why:
|
|
226
|
-
severity:
|
|
304
|
+
procedure: "pillar-assignment-protocol",
|
|
305
|
+
why: "Every epic must name a pillar (hard-blocked at filing). Covers picking an existing HS-PILR theme or authoring a net-new one.",
|
|
306
|
+
severity: "required",
|
|
227
307
|
},
|
|
228
308
|
{
|
|
229
|
-
procedure:
|
|
230
|
-
why:
|
|
231
|
-
severity:
|
|
309
|
+
procedure: "vault-id-assignment-protocol",
|
|
310
|
+
why: "Allocates a unique HS-EPIC-<suffix> id and prevents duplicates.",
|
|
311
|
+
severity: "required",
|
|
232
312
|
},
|
|
233
313
|
{
|
|
234
|
-
procedure:
|
|
235
|
-
why:
|
|
236
|
-
severity:
|
|
314
|
+
procedure: "plan-ingest-agent-workflow",
|
|
315
|
+
why: "Linkage + manifest registration steps when ingesting an external plan into the epic.",
|
|
316
|
+
severity: "recommended",
|
|
237
317
|
},
|
|
238
318
|
{
|
|
239
|
-
procedure:
|
|
240
|
-
why:
|
|
241
|
-
severity:
|
|
319
|
+
procedure: "builder-fleet-capability-reference",
|
|
320
|
+
why: "Optional builder_profile + ## Builder fleet notes at epic filing — anticipate local builder model class and context tier.",
|
|
321
|
+
severity: "recommended",
|
|
242
322
|
},
|
|
243
323
|
],
|
|
244
324
|
file_task: [
|
|
245
325
|
{
|
|
246
|
-
procedure:
|
|
247
|
-
why:
|
|
248
|
-
severity:
|
|
326
|
+
procedure: "spec-hierarchy",
|
|
327
|
+
why: "Defines task frontmatter, required parent-epic link, and child-task fan-out rules.",
|
|
328
|
+
severity: "required",
|
|
249
329
|
},
|
|
250
330
|
{
|
|
251
|
-
procedure:
|
|
252
|
-
why:
|
|
253
|
-
severity:
|
|
331
|
+
procedure: "builder-fleet-capability-reference",
|
|
332
|
+
why: "Required ## Builder brief + dispatch frontmatter (suggested_model, recommended_num_ctx, context_files) for fleet-calibrated tasks.",
|
|
333
|
+
severity: "required",
|
|
254
334
|
},
|
|
255
335
|
{
|
|
256
|
-
procedure:
|
|
257
|
-
why:
|
|
258
|
-
severity:
|
|
336
|
+
procedure: "spec-summary-authoring",
|
|
337
|
+
why: "Every task needs a plain-language summary at the /hs-next breakdown moment — pass summary to heyspex_file_spec / POST /api/specs. Tasks are never filed during plan ingest.",
|
|
338
|
+
severity: "required",
|
|
259
339
|
},
|
|
260
340
|
{
|
|
261
|
-
procedure:
|
|
262
|
-
why:
|
|
263
|
-
severity:
|
|
341
|
+
procedure: "vault-id-assignment-protocol",
|
|
342
|
+
why: "Allocates a unique HS-TASK-<suffix> id and prevents duplicates.",
|
|
343
|
+
severity: "required",
|
|
264
344
|
},
|
|
265
345
|
{
|
|
266
|
-
procedure:
|
|
267
|
-
why:
|
|
268
|
-
severity:
|
|
346
|
+
procedure: "checks-manifest-authoring",
|
|
347
|
+
why: "Every task ships a FROZEN specs/checks/<task-id>.checks.json — each AC classified machine|property|human with named runnable checks, authored at breakdown before any builder sees the brief. A task without one is not ready to dispatch.",
|
|
348
|
+
severity: "required",
|
|
269
349
|
},
|
|
270
350
|
{
|
|
271
|
-
procedure:
|
|
272
|
-
why:
|
|
273
|
-
severity:
|
|
351
|
+
procedure: "unit-test-authoring-protocol",
|
|
352
|
+
why: "How the tests behind the manifest checks are written: pure-core/thin-shell, colocation, the five case families, and the mutation gate as the quality oracle.",
|
|
353
|
+
severity: "recommended",
|
|
274
354
|
},
|
|
275
355
|
],
|
|
276
356
|
file_diagnose: [
|
|
277
357
|
{
|
|
278
|
-
procedure:
|
|
279
|
-
why:
|
|
280
|
-
severity:
|
|
358
|
+
procedure: "spec-hierarchy",
|
|
359
|
+
why: "Defines diagnose frontmatter, the standalone-default / optional epic-or-milestone parent rule, and severity.",
|
|
360
|
+
severity: "required",
|
|
281
361
|
},
|
|
282
362
|
{
|
|
283
|
-
procedure:
|
|
284
|
-
why:
|
|
285
|
-
severity:
|
|
363
|
+
procedure: "vault-id-assignment-protocol",
|
|
364
|
+
why: "Allocates a unique HS-DIAG-<suffix> id and prevents duplicates.",
|
|
365
|
+
severity: "required",
|
|
286
366
|
},
|
|
287
367
|
{
|
|
288
|
-
procedure:
|
|
289
|
-
why:
|
|
290
|
-
severity:
|
|
368
|
+
procedure: "resolution-diagnose-workflow",
|
|
369
|
+
why: "The end-to-end bug→diagnose procedure: intake, §Step 2 symptom-vs-core classification (if fixing exactly this leaves the same class of failure possible, you are still on the symptom), severity ranking, §Step 6 sibling sweep — which now owes a receipt in the body's ## Sweep section — and close-out.",
|
|
370
|
+
severity: "required",
|
|
291
371
|
},
|
|
292
372
|
{
|
|
293
|
-
procedure:
|
|
373
|
+
procedure: "checks-manifest-authoring",
|
|
294
374
|
why: 'A bug is the case where a regression check is worth the most and costs the least — the reproduction is already written. Freeze one runnable check per acceptance criterion via `checks:` on heyspex_file_spec, and make at least one of them FAIL when the fix is reverted; "the test passes now" is also what a symptom-level fix produces. REQUIRED WITH AN OUT: a trivial bug may file with no manifest — filing still succeeds and returns a `no_checks_manifest` warning — but skipping is a decision to state, not a step to forget.',
|
|
295
|
-
severity:
|
|
375
|
+
severity: "required",
|
|
296
376
|
},
|
|
297
377
|
],
|
|
298
378
|
file_research: [
|
|
299
379
|
{
|
|
300
|
-
procedure:
|
|
301
|
-
why:
|
|
302
|
-
severity:
|
|
380
|
+
procedure: "capture-guardrails",
|
|
381
|
+
why: "A brand-new idea surfacing in conversation (no existing HS-CAP-*/HS-RES-* id already in play) goes through Capture → Ground=Expand → Promote-to-research first via heyspex_active_capture — never straight to file_research/research dispatch tooling. Skip only when the research target already has an existing capture or research id.",
|
|
382
|
+
severity: "recommended",
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
procedure: "spec-hierarchy",
|
|
386
|
+
why: "Confirms research is a standalone entity (HS-RES-<id>), how it links back to consumers, and that promotion defaults to a NEW milestone.",
|
|
387
|
+
severity: "required",
|
|
303
388
|
},
|
|
304
389
|
{
|
|
305
|
-
procedure:
|
|
306
|
-
why:
|
|
307
|
-
severity:
|
|
390
|
+
procedure: "research-capture-protocol",
|
|
391
|
+
why: "Required template (Executive Summary + Sources) and the back-wikilink + maintenance rules for a research doc.",
|
|
392
|
+
severity: "required",
|
|
308
393
|
},
|
|
309
394
|
{
|
|
310
|
-
procedure:
|
|
311
|
-
why:
|
|
312
|
-
severity:
|
|
395
|
+
procedure: "vault-id-assignment-protocol",
|
|
396
|
+
why: "Allocates a unique HS-RES-<suffix> id and prevents duplicates.",
|
|
397
|
+
severity: "required",
|
|
313
398
|
},
|
|
314
399
|
],
|
|
315
400
|
file_memory_lesson: [
|
|
316
401
|
{
|
|
317
|
-
procedure:
|
|
318
|
-
why:
|
|
319
|
-
severity:
|
|
402
|
+
procedure: "memory-protocol",
|
|
403
|
+
why: "How to file a memory-lesson and link it back to the originating spec; the four-axis capture rubric.",
|
|
404
|
+
severity: "required",
|
|
320
405
|
},
|
|
321
406
|
{
|
|
322
|
-
procedure:
|
|
323
|
-
why:
|
|
324
|
-
severity:
|
|
407
|
+
procedure: "vault-id-assignment-protocol",
|
|
408
|
+
why: "Allocates a unique memory slug/id and prevents duplicates.",
|
|
409
|
+
severity: "required",
|
|
325
410
|
},
|
|
326
411
|
],
|
|
327
412
|
file_memory_decision: [
|
|
328
413
|
{
|
|
329
|
-
procedure:
|
|
330
|
-
why:
|
|
331
|
-
severity:
|
|
414
|
+
procedure: "memory-protocol",
|
|
415
|
+
why: "How to file a memory-decision and link it back to the originating spec; the four-axis capture rubric.",
|
|
416
|
+
severity: "required",
|
|
332
417
|
},
|
|
333
418
|
{
|
|
334
|
-
procedure:
|
|
335
|
-
why:
|
|
336
|
-
severity:
|
|
419
|
+
procedure: "vault-id-assignment-protocol",
|
|
420
|
+
why: "Allocates a unique memory slug/id and prevents duplicates.",
|
|
421
|
+
severity: "required",
|
|
337
422
|
},
|
|
338
423
|
],
|
|
339
424
|
file_memory_reference: [
|
|
340
425
|
{
|
|
341
|
-
procedure:
|
|
342
|
-
why:
|
|
343
|
-
severity:
|
|
426
|
+
procedure: "memory-protocol",
|
|
427
|
+
why: "How to file a memory-reference and link it back to the originating spec; the four-axis capture rubric.",
|
|
428
|
+
severity: "required",
|
|
344
429
|
},
|
|
345
430
|
{
|
|
346
|
-
procedure:
|
|
347
|
-
why:
|
|
348
|
-
severity:
|
|
431
|
+
procedure: "vault-id-assignment-protocol",
|
|
432
|
+
why: "Allocates a unique memory slug/id and prevents duplicates.",
|
|
433
|
+
severity: "required",
|
|
349
434
|
},
|
|
350
435
|
],
|
|
351
436
|
file_memory_ui_pattern: [
|
|
352
437
|
{
|
|
353
|
-
procedure:
|
|
354
|
-
why:
|
|
355
|
-
severity:
|
|
438
|
+
procedure: "memory-protocol",
|
|
439
|
+
why: "How to file a memory-ui-pattern and link it back to the originating spec; the four-axis capture rubric.",
|
|
440
|
+
severity: "required",
|
|
356
441
|
},
|
|
357
442
|
{
|
|
358
|
-
procedure:
|
|
359
|
-
why:
|
|
360
|
-
severity:
|
|
443
|
+
procedure: "vault-id-assignment-protocol",
|
|
444
|
+
why: "Allocates a unique memory slug/id and prevents duplicates.",
|
|
445
|
+
severity: "required",
|
|
361
446
|
},
|
|
362
447
|
],
|
|
363
448
|
file_review: [
|
|
364
449
|
{
|
|
365
|
-
procedure:
|
|
366
|
-
why:
|
|
367
|
-
severity:
|
|
450
|
+
procedure: "session-close-protocol",
|
|
451
|
+
why: "The HS-REV companion doc shape (four required sections) and the close-out flow it belongs to.",
|
|
452
|
+
severity: "required",
|
|
368
453
|
},
|
|
369
454
|
{
|
|
370
|
-
procedure:
|
|
455
|
+
procedure: "spec-hierarchy",
|
|
371
456
|
why: 'Confirms the review parents the spec under review (parent: "[[<spec>]]" is required).',
|
|
372
|
-
severity:
|
|
457
|
+
severity: "recommended",
|
|
373
458
|
},
|
|
374
459
|
],
|
|
375
460
|
file_capture: [
|
|
376
461
|
{
|
|
377
|
-
procedure:
|
|
378
|
-
why:
|
|
379
|
-
severity:
|
|
462
|
+
procedure: "capture-guardrails",
|
|
463
|
+
why: "Captures are session-based and NOT research — human words stay verbatim in `## Notes`; AI authors separate layers on an explicit pass. Drive them via heyspex_active_capture, not heyspex_file_spec.",
|
|
464
|
+
severity: "required",
|
|
380
465
|
},
|
|
381
466
|
],
|
|
382
467
|
destructive_action: [
|
|
383
468
|
{
|
|
384
|
-
procedure:
|
|
385
|
-
why:
|
|
386
|
-
severity:
|
|
469
|
+
procedure: "destructive-action-checklist",
|
|
470
|
+
why: "Run before any PR that deletes, archives, or routes data-destroying actions.",
|
|
471
|
+
severity: "required",
|
|
387
472
|
},
|
|
388
473
|
{
|
|
389
|
-
procedure:
|
|
390
|
-
why:
|
|
391
|
-
severity:
|
|
474
|
+
procedure: "dev-server-lifecycle-and-db-safety",
|
|
475
|
+
why: "If the action is a DB migration / Neon branch reset / .next delete, stop the running dev server first — check ground-truth status via heyspex_dev_server_status.",
|
|
476
|
+
severity: "recommended",
|
|
392
477
|
},
|
|
393
478
|
],
|
|
394
479
|
protocol_edit: [
|
|
395
480
|
{
|
|
396
|
-
procedure:
|
|
481
|
+
procedure: "spec-hierarchy",
|
|
397
482
|
why: '§Agent autonomy row "Modify a protocol doc" — protocol edits require explicit user approval.',
|
|
398
|
-
severity:
|
|
483
|
+
severity: "required",
|
|
399
484
|
},
|
|
400
485
|
],
|
|
401
486
|
memory_creation: [
|
|
402
487
|
{
|
|
403
|
-
procedure:
|
|
404
|
-
why:
|
|
405
|
-
severity:
|
|
488
|
+
procedure: "memory-protocol",
|
|
489
|
+
why: "How to file a lesson/decision/reference/ui-pattern memory and link it back to the originating spec.",
|
|
490
|
+
severity: "required",
|
|
406
491
|
},
|
|
407
492
|
],
|
|
408
493
|
close_session: [
|
|
409
494
|
{
|
|
410
|
-
procedure:
|
|
411
|
-
why:
|
|
412
|
-
severity:
|
|
495
|
+
procedure: "session-close-protocol",
|
|
496
|
+
why: "Required agent close-out checklist (status flips, review-doc creation, hand-off).",
|
|
497
|
+
severity: "required",
|
|
413
498
|
},
|
|
414
499
|
{
|
|
415
|
-
procedure:
|
|
416
|
-
why:
|
|
417
|
-
severity:
|
|
500
|
+
procedure: "memory-protocol",
|
|
501
|
+
why: "Self-check for learnings is step 0 of close-out — capture any non-obvious lesson/decision as a memory spec.",
|
|
502
|
+
severity: "recommended",
|
|
418
503
|
},
|
|
419
504
|
],
|
|
420
505
|
context_rollover: [
|
|
421
506
|
{
|
|
422
|
-
procedure:
|
|
423
|
-
why:
|
|
424
|
-
severity:
|
|
507
|
+
procedure: "context-rollover-checkpoint",
|
|
508
|
+
why: "Lightweight mid-flight checkpoint when compaction interrupts implementation — record WIP + handoff, then resume. Do NOT flip status / write HS-REV (that is session close, not rollover).",
|
|
509
|
+
severity: "required",
|
|
425
510
|
},
|
|
426
511
|
],
|
|
427
512
|
claim_spec: [
|
|
428
513
|
{
|
|
429
|
-
procedure:
|
|
430
|
-
why:
|
|
431
|
-
severity:
|
|
514
|
+
procedure: "spec-hierarchy",
|
|
515
|
+
why: "A spec is claimable iff it is its own current ClaimTarget — read §Claim eligibility before claiming an Epic/Milestone with open descendants; the console endpoint returns 409 claim_blocked_by_descendants otherwise ([[HS-TASK-0RVD]]).",
|
|
516
|
+
severity: "required",
|
|
432
517
|
},
|
|
433
518
|
{
|
|
434
|
-
procedure:
|
|
435
|
-
why:
|
|
436
|
-
severity:
|
|
519
|
+
procedure: "pre-execution-alignment-gate",
|
|
520
|
+
why: "The /hs-next surface routes the claim through this gate; the brief sections (world_state_new / world_state_modified / graph_delta) and the 4-option menu are documented here.",
|
|
521
|
+
severity: "recommended",
|
|
437
522
|
},
|
|
438
523
|
],
|
|
439
524
|
execute_spec: [
|
|
440
525
|
{
|
|
441
|
-
procedure:
|
|
442
|
-
why:
|
|
443
|
-
severity:
|
|
526
|
+
procedure: "dev-factory-delegation-routing",
|
|
527
|
+
why: "The COO operating contract: delegation is table-driven. Before delegating build/breakdown/critic work, consult dev-factory/routing-table.json — build → free local fleet, breakdown/critic → DeepSeek V4, cloud/Opus escalation-only. NEVER spawn a Claude subagent on your own (expensive) tier for mechanical, gate-verified build.",
|
|
528
|
+
severity: "required",
|
|
444
529
|
},
|
|
445
530
|
{
|
|
446
|
-
procedure:
|
|
447
|
-
why:
|
|
448
|
-
severity:
|
|
531
|
+
procedure: "pre-execution-alignment-gate",
|
|
532
|
+
why: "The first pre-flight before executing an epic/milestone — break down a childless node or revisit a drifted breakdown; hard gate with override-with-reason.",
|
|
533
|
+
severity: "required",
|
|
449
534
|
},
|
|
450
535
|
{
|
|
451
|
-
procedure:
|
|
452
|
-
why:
|
|
453
|
-
severity:
|
|
536
|
+
procedure: "spec-hierarchy",
|
|
537
|
+
why: "Confirms the level you are executing and the parent/child linkage the breakdown must respect.",
|
|
538
|
+
severity: "recommended",
|
|
454
539
|
},
|
|
455
540
|
{
|
|
456
|
-
procedure:
|
|
457
|
-
why:
|
|
458
|
-
severity:
|
|
541
|
+
procedure: "builder-fleet-capability-reference",
|
|
542
|
+
why: "Context tier ladder + task Builder brief contract when breaking down or dispatching to the local fleet.",
|
|
543
|
+
severity: "recommended",
|
|
459
544
|
},
|
|
460
545
|
{
|
|
461
|
-
procedure:
|
|
462
|
-
why:
|
|
463
|
-
severity:
|
|
546
|
+
procedure: "unit-test-authoring-protocol",
|
|
547
|
+
why: "Before writing any test: pure-core/thin-shell structure, colocated <module>.test.ts, the five case families every pure module covers, and the mutation gate as the test-quality oracle.",
|
|
548
|
+
severity: "recommended",
|
|
464
549
|
},
|
|
465
550
|
{
|
|
466
|
-
procedure:
|
|
467
|
-
why:
|
|
468
|
-
severity:
|
|
551
|
+
procedure: "checks-manifest-authoring",
|
|
552
|
+
why: "The frozen specs/checks/<task-id>.checks.json is the external completion gate — builders satisfy its named checks and never edit it; hs check run <task-id> is the per-criterion verdict.",
|
|
553
|
+
severity: "recommended",
|
|
469
554
|
},
|
|
470
555
|
{
|
|
471
|
-
procedure:
|
|
472
|
-
why:
|
|
473
|
-
severity:
|
|
556
|
+
procedure: "dev-server-lifecycle-and-db-safety",
|
|
557
|
+
why: "Execution often involves DB migrations / dependency installs / .next deletes that break a running dev server — check ground-truth status (heyspex_dev_server_status) and stop the server first.",
|
|
558
|
+
severity: "recommended",
|
|
474
559
|
},
|
|
475
560
|
],
|
|
476
561
|
exit_plan_mode: [
|
|
477
562
|
{
|
|
478
|
-
procedure:
|
|
479
|
-
why:
|
|
480
|
-
severity:
|
|
563
|
+
procedure: "plan-quality-canonical-check",
|
|
564
|
+
why: "Run dual overlap+code audit and canonical/supersession check before exit; revise plan to V2.",
|
|
565
|
+
severity: "required",
|
|
481
566
|
},
|
|
482
567
|
{
|
|
483
|
-
procedure:
|
|
484
|
-
why:
|
|
485
|
-
severity:
|
|
568
|
+
procedure: "plan-ingest-agent-workflow",
|
|
569
|
+
why: "After the plan is approved, do NOT start coding. First ingest the approved plan into the vault as an epic (file it with children:[]; tasks fan out later via /hs-next) AND claim it via heyspex_claim_spec before executing. ExitPlanMode is the plan→execute seam — without this step agents skip filing+claiming and go straight to code.",
|
|
570
|
+
severity: "required",
|
|
486
571
|
},
|
|
487
572
|
{
|
|
488
|
-
procedure:
|
|
489
|
-
why:
|
|
490
|
-
severity:
|
|
573
|
+
procedure: "supersession-protocol",
|
|
574
|
+
why: "If the plan deprecates an existing spec, file the supersession edge per protocol.",
|
|
575
|
+
severity: "recommended",
|
|
491
576
|
},
|
|
492
577
|
],
|
|
493
578
|
recommend_location: [
|
|
494
579
|
{
|
|
495
|
-
procedure:
|
|
496
|
-
why:
|
|
497
|
-
severity:
|
|
580
|
+
procedure: "rule-placement-protocol",
|
|
581
|
+
why: "Cost ranking for where a new rule belongs: procedure ≥ memory > spec > CLAUDE.md. CLAUDE.md is the most expensive surface (always-loaded, customer-inherited) — never the default.",
|
|
582
|
+
severity: "required",
|
|
498
583
|
},
|
|
499
584
|
{
|
|
500
|
-
procedure:
|
|
501
|
-
why:
|
|
502
|
-
severity:
|
|
585
|
+
procedure: "memory-protocol",
|
|
586
|
+
why: "When the right home is a memory (rank 2 in rule-placement-protocol), this defines the lesson/decision/reference/ui-pattern shape and tagging conventions.",
|
|
587
|
+
severity: "recommended",
|
|
503
588
|
},
|
|
504
589
|
{
|
|
505
|
-
procedure:
|
|
506
|
-
why:
|
|
507
|
-
severity:
|
|
590
|
+
procedure: "foundations-placement-protocol",
|
|
591
|
+
why: "When the thing to place is a high-level vision/strategy concept (a principle, a north-star idea, a business claim) rather than an agent-behavior rule: ranks the foundations homes (North Star > Operating Model > Principles > Architecture > Positioning > Research) and gates a foundational edit behind user sign-off.",
|
|
592
|
+
severity: "recommended",
|
|
508
593
|
},
|
|
509
594
|
],
|
|
510
595
|
focus_pivot: [
|
|
511
596
|
{
|
|
512
|
-
procedure:
|
|
597
|
+
procedure: "focus-intent-routing",
|
|
513
598
|
why: 'How to detect a roadmap focus-pivot utterance and route it through heyspex_set_focus_areas — covers negation handling ("stop working on X" must NOT add X), the closed-tag-vocabulary constraint (heyspex_list_known_tags first), and the confirmation beat before applying.',
|
|
514
|
-
severity:
|
|
599
|
+
severity: "required",
|
|
515
600
|
},
|
|
516
601
|
],
|
|
517
602
|
server_impacting_action: [
|
|
518
603
|
{
|
|
519
|
-
procedure:
|
|
520
|
-
why:
|
|
521
|
-
severity:
|
|
604
|
+
procedure: "dev-server-lifecycle-and-db-safety",
|
|
605
|
+
why: "Before a DB migration, Neon branch reset, dependency install, .next delete, or starting a second dev server: check ground-truth status (heyspex_dev_server_status / `hs server status`) and stop a live server first — running a migration under a live server strands its in-memory schema and forces a restart.",
|
|
606
|
+
severity: "required",
|
|
522
607
|
},
|
|
523
608
|
],
|
|
524
609
|
restructure_content: [
|
|
525
610
|
{
|
|
526
|
-
procedure:
|
|
527
|
-
why:
|
|
528
|
-
severity:
|
|
611
|
+
procedure: "internal-linking-protocol",
|
|
612
|
+
why: "Before re-parenting an epic, editing a milestone epics[] roster, or moving trackable work to a new canonical home — leave a summary + wikilink stub at the old home so wikilinks and lexical retrieval signal are preserved.",
|
|
613
|
+
severity: "required",
|
|
529
614
|
},
|
|
530
615
|
{
|
|
531
|
-
procedure:
|
|
532
|
-
why:
|
|
533
|
-
severity:
|
|
616
|
+
procedure: "section-supersede-protocol",
|
|
617
|
+
why: "Before editing or deleting a section that has gone stale: the three-way test for edit-in-place vs supersede-the-section vs retire-the-whole-document, the stub properties a reviewer checks, and the rule that the repair stays a proposal a person approves — a linked heading is never silently rewritten.",
|
|
618
|
+
severity: "required",
|
|
534
619
|
},
|
|
535
620
|
{
|
|
536
|
-
procedure:
|
|
537
|
-
why:
|
|
538
|
-
severity:
|
|
621
|
+
procedure: "spec-hierarchy",
|
|
622
|
+
why: "Confirms bidirectional linkage shape (epics: ⇄ milestone:, children: ⇄ parent:) that re-parent edits must maintain.",
|
|
623
|
+
severity: "recommended",
|
|
539
624
|
},
|
|
540
625
|
{
|
|
541
|
-
procedure:
|
|
542
|
-
why:
|
|
543
|
-
severity:
|
|
626
|
+
procedure: "supersession-protocol",
|
|
627
|
+
why: "If the move replaces dead guidance (not merely relocates live work), use supersession instead of a cross-reference stub.",
|
|
628
|
+
severity: "recommended",
|
|
629
|
+
},
|
|
630
|
+
{
|
|
631
|
+
procedure: "doc-canonicalization-protocol",
|
|
632
|
+
why: "If the restructure is an evergreen-doc split/merge rather than plan-side re-parent, use doc-canonicalization instead.",
|
|
633
|
+
severity: "recommended",
|
|
544
634
|
},
|
|
545
635
|
],
|
|
546
636
|
};
|
|
@@ -559,10 +649,9 @@ const VALID_INTENTS = Object.keys(PREFLIGHT_INTENT_MAP);
|
|
|
559
649
|
// close_tool; others carry no filing_tool block.
|
|
560
650
|
const CLOSE_TOOL_MAP = {
|
|
561
651
|
close_session: {
|
|
562
|
-
tool:
|
|
563
|
-
minimal_call: { spec_id:
|
|
564
|
-
note:
|
|
565
|
-
'Status SSOT is PATCH /api/specs/:id/status — not frontmatter edits. This tool applies review-grain coercion, parent rollup, and releases the claim. Still run session-close-protocol steps 2–7 (change-log, HS-REV or review seed, WIP, export).',
|
|
652
|
+
tool: "heyspex_submit_for_review",
|
|
653
|
+
minimal_call: { spec_id: "<completed-review-unit-or-task-id>" },
|
|
654
|
+
note: "Status SSOT is PATCH /api/specs/:id/status — not frontmatter edits. This tool applies review-grain coercion, parent rollup, and releases the claim. Still run session-close-protocol steps 2–7 (change-log, HS-REV or review seed, WIP, export).",
|
|
566
655
|
},
|
|
567
656
|
};
|
|
568
657
|
|
|
@@ -572,30 +661,38 @@ function buildCloseTool(intent) {
|
|
|
572
661
|
}
|
|
573
662
|
|
|
574
663
|
const FILE_SPEC_NOTE =
|
|
575
|
-
|
|
664
|
+
"Use this. The server generates frontmatter from the canonical template. Do not compose frontmatter from a sibling spec.";
|
|
576
665
|
const FILING_TOOL_MAP = {
|
|
577
|
-
file_milestone: { type:
|
|
578
|
-
file_epic: {
|
|
579
|
-
|
|
666
|
+
file_milestone: { type: "milestone" },
|
|
667
|
+
file_epic: {
|
|
668
|
+
type: "epic",
|
|
669
|
+
parentKey: "milestone",
|
|
670
|
+
parentExample: "[[HS-MIL-...]]",
|
|
671
|
+
},
|
|
672
|
+
file_task: {
|
|
673
|
+
type: "task",
|
|
674
|
+
parentKey: "parent",
|
|
675
|
+
parentExample: "[[HS-EPIC-...]]",
|
|
676
|
+
},
|
|
580
677
|
file_diagnose: {
|
|
581
|
-
type:
|
|
678
|
+
type: "diagnose",
|
|
582
679
|
note: `${FILE_SPEC_NOTE} By default a diagnose is filed standalone — it has no parent and does not nest under any epic or milestone, so it lives on its own in the Resolution lane rather than inside a planned work item. Keep it standalone for a cross-cutting bug, or one that isn't part of any single epic's or milestone's scope (the common case). Only set parent: "[[HS-EPIC-...]]" or "[[HS-MIL-...]]" when the bug clearly belongs to that epic's or milestone's work and you want it tracked under it.`,
|
|
583
680
|
},
|
|
584
|
-
file_research: { type:
|
|
585
|
-
file_memory_lesson: { type:
|
|
586
|
-
file_memory_decision: { type:
|
|
587
|
-
file_memory_reference: { type:
|
|
588
|
-
file_memory_ui_pattern: { type:
|
|
681
|
+
file_research: { type: "research" },
|
|
682
|
+
file_memory_lesson: { type: "memory-lesson" },
|
|
683
|
+
file_memory_decision: { type: "memory-decision" },
|
|
684
|
+
file_memory_reference: { type: "memory-reference" },
|
|
685
|
+
file_memory_ui_pattern: { type: "memory-ui-pattern" },
|
|
589
686
|
file_review: {
|
|
590
|
-
type:
|
|
591
|
-
parentKey:
|
|
592
|
-
parentExample:
|
|
687
|
+
type: "review",
|
|
688
|
+
parentKey: "parent",
|
|
689
|
+
parentExample: "[[<spec-under-review>]]",
|
|
593
690
|
note: `${FILE_SPEC_NOTE} A review's parent is the spec it reviews — parent is required.`,
|
|
594
691
|
},
|
|
595
692
|
file_capture: {
|
|
596
|
-
tool:
|
|
597
|
-
minimal_call: { spec_id:
|
|
598
|
-
note:
|
|
693
|
+
tool: "heyspex_active_capture",
|
|
694
|
+
minimal_call: { spec_id: "<HS-CAP-...>" },
|
|
695
|
+
note: "Captures are session-based; use heyspex_active_capture, NOT heyspex_file_spec.",
|
|
599
696
|
},
|
|
600
697
|
};
|
|
601
698
|
|
|
@@ -606,13 +703,17 @@ function buildFilingTool(intent) {
|
|
|
606
703
|
if (!spec) return null;
|
|
607
704
|
// Captures (and any future bespoke tool) carry an explicit shape.
|
|
608
705
|
if (spec.tool) {
|
|
609
|
-
return {
|
|
706
|
+
return {
|
|
707
|
+
tool: spec.tool,
|
|
708
|
+
minimal_call: spec.minimal_call,
|
|
709
|
+
note: spec.note,
|
|
710
|
+
};
|
|
610
711
|
}
|
|
611
712
|
const minimal_call = { type: spec.type };
|
|
612
713
|
if (spec.parentKey) minimal_call[spec.parentKey] = spec.parentExample;
|
|
613
|
-
minimal_call.name =
|
|
714
|
+
minimal_call.name = "<title>";
|
|
614
715
|
return {
|
|
615
|
-
tool:
|
|
716
|
+
tool: "heyspex_file_spec",
|
|
616
717
|
minimal_call,
|
|
617
718
|
note: spec.note ?? FILE_SPEC_NOTE,
|
|
618
719
|
};
|
|
@@ -630,81 +731,81 @@ function buildFilingTool(intent) {
|
|
|
630
731
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
631
732
|
const CLOSE_SESSION_THREAD_PATHS = {
|
|
632
733
|
capture_only: {
|
|
633
|
-
label:
|
|
734
|
+
label: "Capture-only — brain-offload via /hs-capture; no claim, no code",
|
|
634
735
|
is_close: false,
|
|
635
736
|
close_tool: false,
|
|
636
|
-
procedures: [
|
|
737
|
+
procedures: ["research-capture-protocol"],
|
|
637
738
|
steps: [
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
739
|
+
"No session close-out is owed — a capture is not a review unit.",
|
|
740
|
+
"Exit = promote to research (heyspex_promote_capture) or leave the capture pinned.",
|
|
741
|
+
"Do NOT submit_for_review, mint an HS-REV, record a WIP ledger, or flush an audit export.",
|
|
641
742
|
],
|
|
642
743
|
},
|
|
643
744
|
research_only: {
|
|
644
|
-
label:
|
|
745
|
+
label: "Research-only — saved an HS-RES; no claimed spec",
|
|
645
746
|
is_close: false,
|
|
646
747
|
close_tool: false,
|
|
647
|
-
procedures: [
|
|
748
|
+
procedures: ["research-capture-protocol"],
|
|
648
749
|
steps: [
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
750
|
+
"Save findings via heyspex_save_research / heyspex_complete_research (external scope needs >=1 cited URL).",
|
|
751
|
+
"Research is evergreen: do NOT submit_for_review or flip status, and do NOT mint an HS-REV.",
|
|
752
|
+
"No execution WIP ledger is owed. Audit export (heyspex_export_events) is optional.",
|
|
652
753
|
],
|
|
653
754
|
},
|
|
654
755
|
plan_ingest_only: {
|
|
655
|
-
label:
|
|
756
|
+
label: "Plan/ingest-only — filed milestone+epics; no task execution",
|
|
656
757
|
is_close: false,
|
|
657
758
|
close_tool: false,
|
|
658
|
-
procedures: [
|
|
759
|
+
procedures: ["plan-ingest-agent-workflow", "spec-hierarchy"],
|
|
659
760
|
steps: [
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
761
|
+
"Confirm every new spec is registered + linked: heyspex_validate_linkage (heyspex_register_spec for any orphan).",
|
|
762
|
+
"Confirm each milestone/epic carries an authored summary and a re-derived ## Intended outcome.",
|
|
763
|
+
"No task close, no HS-REV, no execution WIP ledger — epics are filed with children: [].",
|
|
663
764
|
],
|
|
664
765
|
},
|
|
665
766
|
one_task: {
|
|
666
|
-
label:
|
|
767
|
+
label: "One-task execution — claimed and completed a single task",
|
|
667
768
|
is_close: true,
|
|
668
769
|
close_tool: true,
|
|
669
|
-
procedures: [
|
|
770
|
+
procedures: ["session-close-protocol", "memory-protocol"],
|
|
670
771
|
steps: [
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
772
|
+
"heyspex_submit_for_review({ spec_id }) on the completed unit (status API SSOT — never hand-edit status:). Grain-coercion, parent rollup, and claim release happen server-side.",
|
|
773
|
+
"Append a change-log roll-up / review seed to the task.",
|
|
774
|
+
"Create an HS-REV companion + parent ## Review back-link ONLY if the active review grain is task; otherwise the change-log seed is enough.",
|
|
775
|
+
"heyspex_record_wip for the worked epic (skip only if the epic closed this session).",
|
|
776
|
+
"heyspex_export_events to flush the audit log. Do NOT git commit / push.",
|
|
676
777
|
],
|
|
677
778
|
},
|
|
678
779
|
multi_task_epic: {
|
|
679
|
-
label:
|
|
780
|
+
label: "Multi-task / epic push — completed several tasks under an epic",
|
|
680
781
|
is_close: true,
|
|
681
782
|
close_tool: true,
|
|
682
|
-
procedures: [
|
|
783
|
+
procedures: ["session-close-protocol", "memory-protocol"],
|
|
683
784
|
steps: [
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
785
|
+
"heyspex_submit_for_review on each completed unit; parent rollup is automatic when the last child lands.",
|
|
786
|
+
"One consolidated change-log + the HS-REV companion at the ACTIVE review grain (epic in steady-state, milestone in MVP).",
|
|
787
|
+
"heyspex_record_wip once for the worked epic; heyspex_export_events; do NOT git commit / push.",
|
|
687
788
|
],
|
|
688
789
|
},
|
|
689
790
|
resolve_diagnose: {
|
|
690
|
-
label:
|
|
791
|
+
label: "Resolve / diagnose — worked an HS-DIAG or a Resolution item",
|
|
691
792
|
is_close: true,
|
|
692
793
|
close_tool: true,
|
|
693
|
-
procedures: [
|
|
794
|
+
procedures: ["session-close-protocol", "resolution-diagnose-workflow"],
|
|
694
795
|
steps: [
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
796
|
+
"If the flag was a stale-cache false positive (the file on disk contradicts it): re-stamp via heyspex_record_alignment — do NOT re-file work that already exists.",
|
|
797
|
+
"If a real diagnose/fix completed: heyspex_submit_for_review on it + change-log; HS-REV per active grain.",
|
|
798
|
+
"heyspex_record_wip if anything remains open; heyspex_export_events.",
|
|
698
799
|
],
|
|
699
800
|
},
|
|
700
801
|
mid_rollover: {
|
|
701
|
-
label:
|
|
802
|
+
label: "Mid-implementation rollover — claimed but not finished",
|
|
702
803
|
is_close: false,
|
|
703
804
|
close_tool: false,
|
|
704
|
-
procedures: [
|
|
805
|
+
procedures: ["context-rollover-checkpoint"],
|
|
705
806
|
steps: [
|
|
706
|
-
|
|
707
|
-
|
|
807
|
+
"This is NOT a session close. Do NOT flip status, mint an HS-REV, or submit_for_review.",
|
|
808
|
+
"Record a lightweight WIP checkpoint (heyspex_record_wip) so the next session resumes; leave the spec in_progress.",
|
|
708
809
|
'See heyspex_preflight({ intent: "context_rollover" }) / the context-rollover-checkpoint procedure for the full checkpoint shape.',
|
|
709
810
|
],
|
|
710
811
|
},
|
|
@@ -713,37 +814,31 @@ const CLOSE_SESSION_THREAD_PATHS = {
|
|
|
713
814
|
const CLOSE_SESSION_THREAD_TYPES = Object.keys(CLOSE_SESSION_THREAD_PATHS);
|
|
714
815
|
|
|
715
816
|
const CLOSE_SESSION_MIXED_RULE =
|
|
716
|
-
|
|
817
|
+
"If the session spanned MORE THAN ONE thread type (e.g. filed a plan AND finished a task), request each applicable type and run the UNION of their steps — ordered, idempotent (re-running submit_for_review / export_events is safe), and surface the ambiguity. Never drop an owed obligation; never duplicate one.";
|
|
717
818
|
|
|
718
819
|
function buildCloseSessionPreflight(vaultRoot, threadType, injectedCatalog) {
|
|
719
820
|
const path = CLOSE_SESSION_THREAD_PATHS[threadType];
|
|
720
821
|
if (!path) {
|
|
721
822
|
return {
|
|
722
|
-
error:
|
|
723
|
-
message: `Unknown thread_type "${threadType}" for close_session. Valid thread types: ${CLOSE_SESSION_THREAD_TYPES.join(
|
|
823
|
+
error: "UNKNOWN_THREAD_TYPE",
|
|
824
|
+
message: `Unknown thread_type "${threadType}" for close_session. Valid thread types: ${CLOSE_SESSION_THREAD_TYPES.join(", ")}.`,
|
|
724
825
|
};
|
|
725
826
|
}
|
|
726
|
-
const catalog =
|
|
827
|
+
const catalog =
|
|
828
|
+
injectedCatalog !== undefined ? injectedCatalog : loadCatalog(vaultRoot);
|
|
727
829
|
if (!catalog) {
|
|
728
830
|
return {
|
|
729
|
-
error:
|
|
831
|
+
error: "CATALOG_NOT_FOUND",
|
|
730
832
|
message:
|
|
731
|
-
|
|
833
|
+
"specs/protocols/.catalog.json not found. Run `node scripts/build-procedure-catalog.mjs`.",
|
|
732
834
|
};
|
|
733
835
|
}
|
|
734
|
-
const procedures = path.procedures.map((name) =>
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
severity: 'required',
|
|
739
|
-
path: entry?.path ?? null,
|
|
740
|
-
title: entry?.title ?? null,
|
|
741
|
-
missing_from_catalog: entry ? undefined : true,
|
|
742
|
-
};
|
|
743
|
-
});
|
|
744
|
-
const close_tool = path.close_tool ? buildCloseTool('close_session') : null;
|
|
836
|
+
const procedures = path.procedures.map((name) =>
|
|
837
|
+
resolveRow(catalog, { procedure: name, severity: "required" }),
|
|
838
|
+
);
|
|
839
|
+
const close_tool = path.close_tool ? buildCloseTool("close_session") : null;
|
|
745
840
|
return {
|
|
746
|
-
intent:
|
|
841
|
+
intent: "close_session",
|
|
747
842
|
thread_type: threadType,
|
|
748
843
|
thread_label: path.label,
|
|
749
844
|
is_close: path.is_close,
|
|
@@ -760,46 +855,37 @@ function buildCloseSessionPreflight(vaultRoot, threadType, injectedCatalog) {
|
|
|
760
855
|
* thread-type path too, so a single HTTP fetch covers both branches).
|
|
761
856
|
*/
|
|
762
857
|
export function executeRunPreflight(vaultRoot, args = {}, injectedCatalog) {
|
|
763
|
-
const intent = String(args.intent ??
|
|
858
|
+
const intent = String(args.intent ?? "").trim();
|
|
764
859
|
if (!intent) {
|
|
765
860
|
return {
|
|
766
|
-
error:
|
|
767
|
-
message: `intent is required. Valid intents: ${VALID_INTENTS.join(
|
|
861
|
+
error: "MISSING_INTENT",
|
|
862
|
+
message: `intent is required. Valid intents: ${VALID_INTENTS.join(", ")}.`,
|
|
768
863
|
};
|
|
769
864
|
}
|
|
770
865
|
// Thread-type-aware close-out: when a thread_type is declared, return that
|
|
771
866
|
// type's CONDITIONAL close path instead of the static checklist ([[HS-RES-32MJ]]).
|
|
772
|
-
const threadType = String(args.thread_type ??
|
|
773
|
-
if (intent ===
|
|
867
|
+
const threadType = String(args.thread_type ?? "").trim();
|
|
868
|
+
if (intent === "close_session" && threadType) {
|
|
774
869
|
return buildCloseSessionPreflight(vaultRoot, threadType, injectedCatalog);
|
|
775
870
|
}
|
|
776
871
|
const recommendations = PREFLIGHT_INTENT_MAP[intent];
|
|
777
872
|
if (!recommendations) {
|
|
778
873
|
return {
|
|
779
|
-
error:
|
|
780
|
-
message: `Unknown intent "${intent}". Valid intents: ${VALID_INTENTS.join(
|
|
874
|
+
error: "UNKNOWN_INTENT",
|
|
875
|
+
message: `Unknown intent "${intent}". Valid intents: ${VALID_INTENTS.join(", ")}.`,
|
|
781
876
|
};
|
|
782
877
|
}
|
|
783
|
-
const catalog =
|
|
878
|
+
const catalog =
|
|
879
|
+
injectedCatalog !== undefined ? injectedCatalog : loadCatalog(vaultRoot);
|
|
784
880
|
if (!catalog) {
|
|
785
881
|
return {
|
|
786
|
-
error:
|
|
882
|
+
error: "CATALOG_NOT_FOUND",
|
|
787
883
|
message:
|
|
788
|
-
|
|
884
|
+
"specs/protocols/.catalog.json not found. Run `node scripts/build-procedure-catalog.mjs`.",
|
|
789
885
|
};
|
|
790
886
|
}
|
|
791
887
|
|
|
792
|
-
const procedures = recommendations.map((row) =>
|
|
793
|
-
const entry = findEntry(catalog, row.procedure);
|
|
794
|
-
return {
|
|
795
|
-
procedure: row.procedure,
|
|
796
|
-
why: row.why,
|
|
797
|
-
severity: row.severity,
|
|
798
|
-
path: entry?.path ?? null,
|
|
799
|
-
title: entry?.title ?? null,
|
|
800
|
-
missing_from_catalog: entry ? undefined : true,
|
|
801
|
-
};
|
|
802
|
-
});
|
|
888
|
+
const procedures = recommendations.map((row) => resolveRow(catalog, row));
|
|
803
889
|
|
|
804
890
|
const filing_tool = buildFilingTool(intent);
|
|
805
891
|
const close_tool = buildCloseTool(intent);
|
|
@@ -814,15 +900,19 @@ export function executeRunPreflight(vaultRoot, args = {}, injectedCatalog) {
|
|
|
814
900
|
// Family-overlap advisory for filing intents: warn agents to check whether
|
|
815
901
|
// a closed family already covered this goal with no iterates_on edge.
|
|
816
902
|
const FILING_ADVISORY =
|
|
817
|
-
|
|
818
|
-
const FILING_INTENTS_WITH_ADVISORY = [
|
|
903
|
+
"Family-overlap check (warn-only): before filing, check whether a CLOSED family already shipped against this goal with no iterates_on: edge — compare both TITLE and JTBD (job-to-be-done) statement, not title alone, since two differently-titled specs can still be the same job (owner-approved 2026-08-23 job-based-duplicate-detection extension) — if so this may be round 2; see entity-model §Iteration lineage. Never blocks filing.";
|
|
904
|
+
const FILING_INTENTS_WITH_ADVISORY = [
|
|
905
|
+
"file_epic",
|
|
906
|
+
"file_task",
|
|
907
|
+
"promote_research",
|
|
908
|
+
];
|
|
819
909
|
if (FILING_INTENTS_WITH_ADVISORY.includes(intent)) {
|
|
820
910
|
result.advisory = FILING_ADVISORY;
|
|
821
911
|
}
|
|
822
912
|
|
|
823
913
|
// Static close_session call (no thread_type declared): nudge toward the
|
|
824
914
|
// conditional path so the agent runs the right minimal-complete close-out.
|
|
825
|
-
if (intent ===
|
|
915
|
+
if (intent === "close_session") {
|
|
826
916
|
result.thread_type_hint = {
|
|
827
917
|
note: 'Close-out is thread-type-aware. Re-call heyspex_preflight({ intent: "close_session", thread_type: "<type>" }) to get the conditional close path for what this session actually did.',
|
|
828
918
|
thread_types: CLOSE_SESSION_THREAD_TYPES.map((t) => ({
|
|
@@ -834,9 +924,64 @@ export function executeRunPreflight(vaultRoot, args = {}, injectedCatalog) {
|
|
|
834
924
|
};
|
|
835
925
|
}
|
|
836
926
|
|
|
927
|
+
// [[HS-EN4F]]: research intents carry the absolute hs-res route.
|
|
928
|
+
if (RESEARCH_TOOLING_INTENTS.includes(intent)) {
|
|
929
|
+
const research_tooling = buildResearchTooling();
|
|
930
|
+
if (research_tooling) result.research_tooling = research_tooling;
|
|
931
|
+
}
|
|
932
|
+
|
|
837
933
|
return result;
|
|
838
934
|
}
|
|
839
935
|
|
|
936
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
937
|
+
// [[HS-EN4F]] — the hs-res route, in absolute paths. The /hs-res command and
|
|
938
|
+
// its SKILL.md spell the gather/synthesis CLIs repo-relative
|
|
939
|
+
// (`node dev-factory/spexcrawl/...`), which only resolves inside the platform
|
|
940
|
+
// checkout. An agent in a customer repo (sales-coach, 2026-10-02) looked for
|
|
941
|
+
// hs-res there, did not find it, concluded "the tooling isn't installed" and
|
|
942
|
+
// fanned out Sonnet subagents on WebSearch. The research preflight is the one
|
|
943
|
+
// call every research path must make (the HS-K0ZP gate refuses WebSearch /
|
|
944
|
+
// WebFetch until it runs), so it carries the route: absolute commands derived
|
|
945
|
+
// from where THIS module sits — the MCP server serving the customer repo runs
|
|
946
|
+
// out of the platform checkout, so these paths resolve from any working
|
|
947
|
+
// directory. Null when the platform does not ship the CLIs (published package).
|
|
948
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
949
|
+
const RESEARCH_TOOLING_INTENTS = [
|
|
950
|
+
"research",
|
|
951
|
+
"file_research",
|
|
952
|
+
"promote_research",
|
|
953
|
+
];
|
|
954
|
+
const DEFAULT_PLATFORM_ROOT = resolve(
|
|
955
|
+
dirname(fileURLToPath(import.meta.url)),
|
|
956
|
+
"..",
|
|
957
|
+
"..",
|
|
958
|
+
);
|
|
959
|
+
|
|
960
|
+
export function buildResearchTooling(
|
|
961
|
+
platformRoot = DEFAULT_PLATFORM_ROOT,
|
|
962
|
+
exists = existsSync,
|
|
963
|
+
) {
|
|
964
|
+
const abs = (...parts) => join(platformRoot, ...parts).replace(/\\/g, "/");
|
|
965
|
+
const spexcrawl = abs("dev-factory", "spexcrawl", "spexcrawl-cli.mjs");
|
|
966
|
+
const firecrawl = abs("dev-factory", "spexcrawl", "firecrawl-cli.mjs");
|
|
967
|
+
const synthesize = abs("dev-factory", "spexcrawl", "synthesize-cli.mjs");
|
|
968
|
+
const skill = abs(".commandcode", "skills", "hs-res", "SKILL.md");
|
|
969
|
+
if (![spexcrawl, firecrawl, synthesize].every((p) => exists(p))) return null;
|
|
970
|
+
return {
|
|
971
|
+
method:
|
|
972
|
+
"hs-res (the confidence ladder) — SpexCrawl via the Companion is primary, self-hosted Firecrawl is the automatic fallback, DeepSeek synthesizes. Zero Claude/Anthropic sub-agents.",
|
|
973
|
+
note: "These commands run from ANY repo. A project with no /hs-res command or skill of its own does NOT mean the tooling is missing — use these absolute paths. Native WebSearch/WebFetch is the last resort only when both gather backends report unavailable; never fan out Sonnet sub-agents for research.",
|
|
974
|
+
skill_path: exists(skill) ? skill : null,
|
|
975
|
+
gather_primary: `node "${spexcrawl}" search "<query>" --json`,
|
|
976
|
+
scrape_primary: `node "${spexcrawl}" scrape "<url>" --json`,
|
|
977
|
+
gather_fallback: `node "${firecrawl}" search "<query>" --json`,
|
|
978
|
+
scrape_fallback: `node "${firecrawl}" scrape "<url>" --json`,
|
|
979
|
+
synthesize: `node "${synthesize}" --prompt-file <path> --json`,
|
|
980
|
+
grounding_first:
|
|
981
|
+
"Before any gather: method triage (known-target-retrieval vs the ladder), ground-first (heyspex_recall_memories), and check for a working reference the owner already runs (e.g. `docker ps` for a local Firecrawl on :3002). Record the four-part grounding result (already built / already decided / owner reference / residual delta).",
|
|
982
|
+
};
|
|
983
|
+
}
|
|
984
|
+
|
|
840
985
|
export const PREFLIGHT_INTENTS = VALID_INTENTS;
|
|
841
986
|
export { CLOSE_SESSION_THREAD_TYPES };
|
|
842
987
|
|
|
@@ -871,12 +1016,17 @@ function catalogFromProceduresRoute(body) {
|
|
|
871
1016
|
* Exported for callers that need the raw catalog object outside the three
|
|
872
1017
|
* higher-level wrappers below (none today; kept for symmetry / testability).
|
|
873
1018
|
*/
|
|
874
|
-
export async function loadCatalogViaTier({
|
|
1019
|
+
export async function loadCatalogViaTier({
|
|
1020
|
+
vaultRoot,
|
|
1021
|
+
tier,
|
|
1022
|
+
consoleFetch,
|
|
1023
|
+
consoleUrl,
|
|
1024
|
+
}) {
|
|
875
1025
|
return readViaSelector({
|
|
876
1026
|
tier,
|
|
877
1027
|
diskRead: () => loadCatalog(vaultRoot),
|
|
878
1028
|
neonRead: async () => {
|
|
879
|
-
const res = await consoleFetch(
|
|
1029
|
+
const res = await consoleFetch("/api/procedures");
|
|
880
1030
|
if (!res.ok) {
|
|
881
1031
|
throw new Error(
|
|
882
1032
|
`GET /api/procedures returned ${res.status}: ${res.error || JSON.stringify(res.body ?? {})}`,
|
|
@@ -884,22 +1034,34 @@ export async function loadCatalogViaTier({ vaultRoot, tier, consoleFetch, consol
|
|
|
884
1034
|
}
|
|
885
1035
|
return catalogFromProceduresRoute(res.body);
|
|
886
1036
|
},
|
|
887
|
-
routeLabel:
|
|
1037
|
+
routeLabel: "GET /api/procedures",
|
|
888
1038
|
consoleUrl,
|
|
889
1039
|
});
|
|
890
1040
|
}
|
|
891
1041
|
|
|
892
1042
|
/** Cloud-reliant sibling of {@link executeListProcedures}. */
|
|
893
|
-
export async function executeListProceduresViaTier({
|
|
1043
|
+
export async function executeListProceduresViaTier({
|
|
1044
|
+
vaultRoot,
|
|
1045
|
+
args,
|
|
1046
|
+
tier,
|
|
1047
|
+
consoleFetch,
|
|
1048
|
+
consoleUrl,
|
|
1049
|
+
}) {
|
|
894
1050
|
return readViaSelector({
|
|
895
1051
|
tier,
|
|
896
1052
|
diskRead: () => executeListProcedures(vaultRoot, args),
|
|
897
1053
|
neonRead: async () => {
|
|
898
|
-
const catalog = await loadCatalogViaTier({
|
|
1054
|
+
const catalog = await loadCatalogViaTier({
|
|
1055
|
+
vaultRoot,
|
|
1056
|
+
tier: "neon",
|
|
1057
|
+
consoleFetch,
|
|
1058
|
+
consoleUrl,
|
|
1059
|
+
});
|
|
899
1060
|
if (!catalog) {
|
|
900
1061
|
return {
|
|
901
|
-
error:
|
|
902
|
-
message:
|
|
1062
|
+
error: "CATALOG_NOT_FOUND",
|
|
1063
|
+
message:
|
|
1064
|
+
"GET /api/procedures returned no catalog (console reports it missing).",
|
|
903
1065
|
};
|
|
904
1066
|
}
|
|
905
1067
|
// Filtering happens HERE, client-side — the route has no tag/trigger/query
|
|
@@ -908,24 +1070,39 @@ export async function executeListProceduresViaTier({ vaultRoot, args, tier, cons
|
|
|
908
1070
|
// catalog as `injectedCatalog` runs the SAME filter logic either way.
|
|
909
1071
|
return executeListProcedures(vaultRoot, args, catalog);
|
|
910
1072
|
},
|
|
911
|
-
routeLabel:
|
|
1073
|
+
routeLabel: "GET /api/procedures",
|
|
912
1074
|
consoleUrl,
|
|
913
1075
|
});
|
|
914
1076
|
}
|
|
915
1077
|
|
|
916
1078
|
/** Cloud-reliant sibling of {@link executeGetProcedure}. Single round trip: `GET /api/procedures/<name>` returns catalog entry + full body together. */
|
|
917
|
-
export async function executeGetProcedureViaTier({
|
|
1079
|
+
export async function executeGetProcedureViaTier({
|
|
1080
|
+
vaultRoot,
|
|
1081
|
+
args,
|
|
1082
|
+
tier,
|
|
1083
|
+
consoleFetch,
|
|
1084
|
+
consoleUrl,
|
|
1085
|
+
}) {
|
|
918
1086
|
return readViaSelector({
|
|
919
1087
|
tier,
|
|
920
1088
|
diskRead: () => executeGetProcedure(vaultRoot, args),
|
|
921
1089
|
neonRead: async () => {
|
|
922
|
-
const name = String(args?.name ??
|
|
1090
|
+
const name = String(args?.name ?? "").trim();
|
|
923
1091
|
if (!name) {
|
|
924
|
-
return {
|
|
1092
|
+
return {
|
|
1093
|
+
error: "MISSING_NAME",
|
|
1094
|
+
message: 'name is required (e.g. "spec-hierarchy")',
|
|
1095
|
+
};
|
|
925
1096
|
}
|
|
926
|
-
const res = await consoleFetch(
|
|
1097
|
+
const res = await consoleFetch(
|
|
1098
|
+
`/api/procedures/${encodeURIComponent(name)}`,
|
|
1099
|
+
);
|
|
927
1100
|
if (res.ok) {
|
|
928
|
-
return {
|
|
1101
|
+
return {
|
|
1102
|
+
procedure: res.body?.procedure,
|
|
1103
|
+
content: res.body?.content,
|
|
1104
|
+
source: res.body?.source ?? null,
|
|
1105
|
+
};
|
|
929
1106
|
}
|
|
930
1107
|
// Aligned with the sibling *ViaTier functions ([[HS-DJWP-MCPC-TASK]]
|
|
931
1108
|
// round 3 fix — a 500 {error:'boom'} was previously swallowed as a real
|
|
@@ -937,34 +1114,46 @@ export async function executeGetProcedureViaTier({ vaultRoot, args, tier, consol
|
|
|
937
1114
|
// selector surfaces CONSOLE_UNREACHABLE_CLOUD_MODE rather than silently
|
|
938
1115
|
// degrading; the epic's no-silent-degrade rule treats "console answered,
|
|
939
1116
|
// but the answer means something is broken" the same as unreachable.
|
|
940
|
-
if (res.status === 404 && res.body?.error ===
|
|
1117
|
+
if (res.status === 404 && res.body?.error === "PROCEDURE_NOT_FOUND") {
|
|
941
1118
|
return { error: res.body.error, message: res.body.message };
|
|
942
1119
|
}
|
|
943
1120
|
throw new Error(
|
|
944
|
-
`GET /api/procedures/${name} returned ${res.status}: ${res.body?.error ? `${res.body.error}${res.body.message ? ` — ${res.body.message}` :
|
|
1121
|
+
`GET /api/procedures/${name} returned ${res.status}: ${res.body?.error ? `${res.body.error}${res.body.message ? ` — ${res.body.message}` : ""}` : res.error || JSON.stringify(res.body ?? {})}`,
|
|
945
1122
|
);
|
|
946
1123
|
},
|
|
947
|
-
routeLabel:
|
|
1124
|
+
routeLabel: "GET /api/procedures/[name]",
|
|
948
1125
|
consoleUrl,
|
|
949
1126
|
});
|
|
950
1127
|
}
|
|
951
1128
|
|
|
952
1129
|
/** Cloud-reliant sibling of {@link executeRunPreflight}. One catalog fetch covers both the static intent map AND the close_session thread-type path. */
|
|
953
|
-
export async function executeRunPreflightViaTier({
|
|
1130
|
+
export async function executeRunPreflightViaTier({
|
|
1131
|
+
vaultRoot,
|
|
1132
|
+
args,
|
|
1133
|
+
tier,
|
|
1134
|
+
consoleFetch,
|
|
1135
|
+
consoleUrl,
|
|
1136
|
+
}) {
|
|
954
1137
|
return readViaSelector({
|
|
955
1138
|
tier,
|
|
956
1139
|
diskRead: () => executeRunPreflight(vaultRoot, args),
|
|
957
1140
|
neonRead: async () => {
|
|
958
|
-
const catalog = await loadCatalogViaTier({
|
|
1141
|
+
const catalog = await loadCatalogViaTier({
|
|
1142
|
+
vaultRoot,
|
|
1143
|
+
tier: "neon",
|
|
1144
|
+
consoleFetch,
|
|
1145
|
+
consoleUrl,
|
|
1146
|
+
});
|
|
959
1147
|
if (!catalog) {
|
|
960
1148
|
return {
|
|
961
|
-
error:
|
|
962
|
-
message:
|
|
1149
|
+
error: "CATALOG_NOT_FOUND",
|
|
1150
|
+
message:
|
|
1151
|
+
"GET /api/procedures returned no catalog (console reports it missing).",
|
|
963
1152
|
};
|
|
964
1153
|
}
|
|
965
1154
|
return executeRunPreflight(vaultRoot, args, catalog);
|
|
966
1155
|
},
|
|
967
|
-
routeLabel:
|
|
1156
|
+
routeLabel: "GET /api/procedures",
|
|
968
1157
|
consoleUrl,
|
|
969
1158
|
});
|
|
970
1159
|
}
|