@holmes-lab/holmes-kit 0.1.6 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/README.md +13 -9
- package/bin/holmes-mcp.js +0 -0
- package/dist/.build-id +1 -1
- package/dist/holmes/cli/agents.d.ts +50 -0
- package/dist/holmes/cli/ci-gate.d.ts +18 -0
- package/dist/holmes/cli/doctor.d.ts +44 -0
- package/dist/holmes/cli/gitignore-merge.d.ts +18 -0
- package/dist/holmes/cli/governed-precondition.d.ts +27 -0
- package/dist/holmes/cli/index.d.ts +14 -0
- package/dist/holmes/cli/index.js +23 -1
- package/dist/holmes/cli/init.d.ts +69 -0
- package/dist/holmes/cli/interactive-prompt.d.ts +9 -0
- package/dist/holmes/cli/playbook-skills.d.ts +129 -0
- package/dist/holmes/cli/roles-readme.d.ts +12 -0
- package/dist/holmes/cli/serve.d.ts +14 -0
- package/dist/holmes/cli/serve.js +70 -0
- package/dist/holmes/cli/settings-merge.d.ts +66 -0
- package/dist/holmes/config/config.d.ts +13 -0
- package/dist/holmes/context/bundler.d.ts +40 -0
- package/dist/holmes/context/render.d.ts +9 -0
- package/dist/holmes/context/tiers.d.ts +54 -0
- package/dist/holmes/context/tokens.d.ts +2 -0
- package/dist/holmes/cpg/ast-mutation.d.ts +31 -0
- package/dist/holmes/cpg/ast-mutation.js +126 -0
- package/dist/holmes/cpg/cpg-scanner.d.ts +69 -0
- package/dist/holmes/cpg/dynamic-cpg.d.ts +18 -0
- package/dist/holmes/cpg/dynamic-cpg.js +82 -0
- package/dist/holmes/cpg/hash-cache.d.ts +21 -0
- package/dist/holmes/cpg/language-parser-walk.d.ts +31 -0
- package/dist/holmes/cpg/language-parser-worker.d.ts +1 -0
- package/dist/holmes/cpg/language-parser.d.ts +116 -0
- package/dist/holmes/cpg/program-slicing.d.ts +21 -0
- package/dist/holmes/cpg/program-slicing.js +123 -0
- package/dist/holmes/cpg/scan-cache.d.ts +21 -0
- package/dist/holmes/cpg/source-path.d.ts +2 -0
- package/dist/holmes/cpg/test-files.d.ts +36 -0
- package/dist/holmes/governance/constitution-debt.d.ts +3 -0
- package/dist/holmes/governance/constitution-report.d.ts +23 -0
- package/dist/holmes/governance/constitution.d.ts +61 -0
- package/dist/holmes/governance/identity.d.ts +28 -0
- package/dist/holmes/governance/ledger-lock.d.ts +55 -0
- package/dist/holmes/governance/ledger-store.conformance.d.ts +11 -0
- package/dist/holmes/governance/ledger-store.d.ts +114 -0
- package/dist/holmes/governance/progress-ledger.d.ts +25 -0
- package/dist/holmes/governance/provenance-chain.d.ts +154 -0
- package/dist/holmes/governance/provenance-ledger.d.ts +102 -0
- package/dist/holmes/governance/provenance-schema.d.ts +21 -0
- package/dist/holmes/governance/replica-id.d.ts +20 -0
- package/dist/holmes/governance/role-policy.d.ts +52 -0
- package/dist/holmes/governance/trust-score.d.ts +32 -0
- package/dist/holmes/guardrail/anchors.d.ts +8 -0
- package/dist/holmes/guardrail/blind-spots.d.ts +32 -0
- package/dist/holmes/guardrail/decision-ledger.d.ts +29 -0
- package/dist/holmes/guardrail/executable-artifact.d.ts +1 -0
- package/dist/holmes/guardrail/governance-history.d.ts +15 -0
- package/dist/holmes/guardrail/phase.d.ts +27 -0
- package/dist/holmes/guardrail/risk-classifier.d.ts +52 -0
- package/dist/holmes/guardrail/risk-gate.d.ts +77 -0
- package/dist/holmes/guardrail/risk-types.d.ts +48 -0
- package/dist/holmes/guardrail/tspec-state.d.ts +71 -0
- package/dist/holmes/guardrail/write-target.d.ts +77 -0
- package/dist/holmes/hooks/adapters/antigravity.d.ts +42 -0
- package/dist/holmes/hooks/pre-tool-use.d.ts +144 -0
- package/dist/holmes/hooks/stop.d.ts +91 -0
- package/dist/holmes/mcp/basis.d.ts +100 -0
- package/dist/holmes/mcp/handlers.d.ts +482 -0
- package/dist/holmes/mcp/server.d.ts +2 -0
- package/dist/holmes/mcp/stdio-client.d.ts +51 -0
- package/dist/holmes/mcp/supervisor.d.ts +48 -0
- package/dist/holmes/mcp/tool-schemas.d.ts +12 -0
- package/dist/holmes/mcp/validate-args.d.ts +27 -0
- package/dist/holmes/messages/registry.d.ts +23 -0
- package/dist/holmes/project/baseline.d.ts +66 -0
- package/dist/holmes/project/change-source.d.ts +89 -0
- package/dist/holmes/project/ignore.d.ts +33 -0
- package/dist/holmes/project/root.d.ts +34 -0
- package/dist/holmes/reverse/anchor.d.ts +47 -0
- package/dist/holmes/reverse/cluster.d.ts +45 -0
- package/dist/holmes/reverse/draft.d.ts +33 -0
- package/dist/holmes/reverse/dynamic-wiring.d.ts +18 -0
- package/dist/holmes/reverse/scan.d.ts +103 -0
- package/dist/holmes/reverse/surface.d.ts +91 -0
- package/dist/holmes/reverse/test-map.d.ts +91 -0
- package/dist/holmes/review/coverage.d.ts +21 -0
- package/dist/holmes/review/findings.d.ts +49 -0
- package/dist/holmes/review/package.d.ts +26 -0
- package/dist/holmes/review/review-targets.d.ts +28 -0
- package/dist/holmes/review/scope.d.ts +13 -0
- package/dist/holmes/review/test-evidence.d.ts +31 -0
- package/dist/holmes/review/test-runner.d.ts +141 -0
- package/dist/holmes/rtm/dataflow-taint.d.ts +81 -0
- package/dist/holmes/rtm/gap-analyzer.d.ts +28 -0
- package/dist/holmes/rtm/git-changes.d.ts +33 -0
- package/dist/holmes/rtm/heatmap.d.ts +21 -0
- package/dist/holmes/rtm/incremental.d.ts +38 -0
- package/dist/holmes/rtm/localize.d.ts +36 -0
- package/dist/holmes/rtm/rtm-builder.d.ts +101 -0
- package/dist/holmes/rtm/rtm-check.d.ts +13 -0
- package/dist/holmes/rtm/rtm-graph.d.ts +88 -0
- package/dist/holmes/rtm/taint.d.ts +35 -0
- package/dist/holmes/rtm/test-scope.d.ts +85 -0
- package/dist/holmes/server/dashboard.d.ts +30 -0
- package/dist/holmes/server/dashboard.js +1758 -0
- package/dist/holmes/spec/approval-blockers.d.ts +62 -0
- package/dist/holmes/spec/breaking-change.d.ts +42 -0
- package/dist/holmes/spec/legacy-format.d.ts +93 -0
- package/dist/holmes/spec/spec-digest.d.ts +29 -0
- package/dist/holmes/spec/spec-parser.d.ts +12 -0
- package/dist/holmes/spec/spec-store.conformance.d.ts +10 -0
- package/dist/holmes/spec/spec-store.d.ts +130 -0
- package/dist/holmes/spec/spec-types.d.ts +150 -0
- package/dist/holmes/spec/validator.d.ts +65 -0
- package/package.json +5 -4
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Spec } from './spec-parser';
|
|
2
|
+
/**
|
|
3
|
+
* Sections that still hold nothing but a generated placeholder.
|
|
4
|
+
*
|
|
5
|
+
* Two generators plant placeholders and both are judged here:
|
|
6
|
+
* - `reverse_draft` writes `TODO — <prompt>` plus the INFERRED marker comment. Both are
|
|
7
|
+
* required together: 11 of this repository's 257 approved specs use the word "TODO" in
|
|
8
|
+
* legitimate prose ("남아 있는 TODO 주석을 제거한다"), and a word search refuses every one.
|
|
9
|
+
* - `spec_create` writes the bare literal `TODO` (`FIELD_PLACEHOLDER`) into each required section.
|
|
10
|
+
* That one is unambiguous on its own — an entire section equal to the string is never prose.
|
|
11
|
+
*/
|
|
12
|
+
export declare function placeholderSections(spec: Spec): string[];
|
|
13
|
+
/**
|
|
14
|
+
* Codes that `validateSpec` reports but approval itself supplies, so they are not blockers.
|
|
15
|
+
* Exported because `spec_approve` filters by the same set — two hand-maintained lists were how the
|
|
16
|
+
* gate and the act drifted apart in the first place.
|
|
17
|
+
*/
|
|
18
|
+
export declare const SUPPLIED_BY_APPROVAL: Set<string>;
|
|
19
|
+
/**
|
|
20
|
+
* Parent-chain reasons an approval is refused.
|
|
21
|
+
*
|
|
22
|
+
* `validateSpec` deliberately stays silent on both (it `continue`s past a draft or unsealed parent —
|
|
23
|
+
* "other findings' business"), so nothing else computes these. Shared with `spec_approve` so the
|
|
24
|
+
* wording cannot drift.
|
|
25
|
+
*/
|
|
26
|
+
/** The one wording for a spec whose prose is still a generator stub. Shared for the same reason. */
|
|
27
|
+
export declare const placeholderMessage: (sections: string[]) => string;
|
|
28
|
+
/** The one wording for an unsealed parent. Shared so a second, degraded copy cannot appear. */
|
|
29
|
+
export declare const unsealedParentMessage: (pid: string) => string;
|
|
30
|
+
export declare function parentBlockers(spec: Spec, resolve: (id: string) => Spec | null): string[];
|
|
31
|
+
/**
|
|
32
|
+
* Every reason `spec` cannot be approved right now. Empty means nothing is in the way.
|
|
33
|
+
*
|
|
34
|
+
* Order matches `spec_approve`'s: parents, then static validity, then the declaration duty, then
|
|
35
|
+
* placeholders. Pure — it reads the spec objects it is handed and touches no filesystem, because
|
|
36
|
+
* the gate calls it from a PreToolUse hook on every tool call.
|
|
37
|
+
*/
|
|
38
|
+
export declare function approvalBlockers(spec: Spec, resolve: (id: string) => Spec | null): string[];
|
|
39
|
+
/** The separator between one spec's blockers. Shared so nothing re-splits on a guessed delimiter. */
|
|
40
|
+
export declare const BLOCKER_SEP = "; ";
|
|
41
|
+
/**
|
|
42
|
+
* One spec's blockers as a labelled clause.
|
|
43
|
+
*
|
|
44
|
+
* Exported so a caller that needs to CLAMP the list works on real blocker boundaries. Review found
|
|
45
|
+
* `tspec-state` re-splitting the joined string on `; ` — a delimiter that also occurs inside
|
|
46
|
+
* author-controlled text (a `depends_on` entry, a section name), which produced a sliced spec id
|
|
47
|
+
* with an unterminated quote and a remainder counting fragments rather than blockers.
|
|
48
|
+
*/
|
|
49
|
+
export declare const blockerClause: (label: string, blockers: string[]) => string;
|
|
50
|
+
/**
|
|
51
|
+
* The blockers as one line for a refusal message, or `null` when nothing blocks.
|
|
52
|
+
*
|
|
53
|
+
* `specId` labels the list. A refusal can name several specs at once, and review found the
|
|
54
|
+
* unlabelled form reintroduced exactly what REQ-155 forbids: the reader saw two reason-lists run
|
|
55
|
+
* together with no way to tell which spec each belonged to.
|
|
56
|
+
*
|
|
57
|
+
* Blockers are separated by `; ` rather than ` / ` because section names legitimately contain a
|
|
58
|
+
* slash (`Inputs / Outputs`), which made the boundaries unreadable.
|
|
59
|
+
*
|
|
60
|
+
* Never throws: a refusal that says less is recoverable, a hook that dies is not.
|
|
61
|
+
*/
|
|
62
|
+
export declare function blockerSummary(spec: Spec | undefined, resolve: (id: string) => Spec | null, specId?: string): string | null;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Spec } from './spec-parser';
|
|
2
|
+
/**
|
|
3
|
+
* ADR-013's enforcement device: the obligation to declare a breaking change lives on the ACT of
|
|
4
|
+
* approval, not on the document's static validity.
|
|
5
|
+
*
|
|
6
|
+
* WHY here and not in `validateSpec`'s `requiredFields`, measured rather than argued: all 38 governed
|
|
7
|
+
* A-SPECs in this repository are already approved, so adding the field to the state predicate
|
|
8
|
+
* produced **38 ART-3 violations and `evaluateStop().block === true`** — the harness would have
|
|
9
|
+
* blocked every turn, bricked by the very work meant to strengthen its governance.
|
|
10
|
+
*
|
|
11
|
+
* The move is not a workaround, it is the correct reading. "Is this change breaking?" is not a static
|
|
12
|
+
* property a document either has or lacks; it is a question that must be answered at the moment a
|
|
13
|
+
* change is sealed. Re-sealing means the content changed, which is exactly when the question is due.
|
|
14
|
+
*
|
|
15
|
+
* CONVERGENCE: any spec that changes must be re-approved and therefore acquires the field. A spec
|
|
16
|
+
* that never changes never acquires it — and cannot introduce a breaking change either. That is why
|
|
17
|
+
* no backfill is needed, and why mechanically filling 38 documents with `none` would be the very
|
|
18
|
+
* formulaic answer this device exists to discourage.
|
|
19
|
+
*
|
|
20
|
+
* REMOVAL AFTER APPROVAL is not defended here on purpose: deleting the field changes the body, the
|
|
21
|
+
* `approved_digest` stops matching, and the existing seal machinery reports `post-approval-edit`.
|
|
22
|
+
* Adding a second mechanism for a case the first already covers would be duplication, not depth.
|
|
23
|
+
*/
|
|
24
|
+
/** ADR-013's four tiers, plus `none`. Order is the order the denial message lists them in. */
|
|
25
|
+
export declare const BREAKING_CHANGE_GRADES: readonly ["none", "persisted-artifact", "derived-artifact", "code-interface", "gate-behavior"];
|
|
26
|
+
export type BreakingChangeGrade = (typeof BREAKING_CHANGE_GRADES)[number];
|
|
27
|
+
/**
|
|
28
|
+
* `null` when the spec satisfies ADR-013's declaration duty, otherwise the reason it does not.
|
|
29
|
+
*
|
|
30
|
+
* Only A-SPEC is checked: it is the layer that carries the implementation contract (`Files to
|
|
31
|
+
* Touch`), so it is the layer where a change becomes breaking. Widening this to every spec type
|
|
32
|
+
* would collect answers from documents that touch no artefact.
|
|
33
|
+
*/
|
|
34
|
+
export declare function checkBreakingChangeDeclared(spec: Spec): string | null;
|
|
35
|
+
/**
|
|
36
|
+
* Grade counts across the governed A-SPECs, with `(unset)` for those that have not been re-approved
|
|
37
|
+
* since ADR-013. A REPORT, never an assertion: "all `none` must fail" would fire falsely during a
|
|
38
|
+
* period when every change genuinely is non-breaking. ADR-013 recorded formulaic-`none` as a revisit
|
|
39
|
+
* trigger for a human to judge, and this surfaces the distribution that judgement needs — including
|
|
40
|
+
* how far the corpus has converged.
|
|
41
|
+
*/
|
|
42
|
+
export declare function breakingChangeDistribution(specs: Spec[]): Record<string, number>;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { Spec } from './spec-parser';
|
|
2
|
+
import { SpecType } from './spec-types';
|
|
3
|
+
/**
|
|
4
|
+
* How a spec document relates to the CURRENT format — the compatibility policy of REQ-184.
|
|
5
|
+
*
|
|
6
|
+
* holmes-kit will change its spec format again. This module fixes the answer to "what do we do with
|
|
7
|
+
* documents written under an older one" so it does not have to be re-argued each time:
|
|
8
|
+
*
|
|
9
|
+
* read but do not enforce → a document whose kind is knowable but undeclared
|
|
10
|
+
* enforce → a document a human explicitly upgraded
|
|
11
|
+
* read and leave alone → a kind this tool no longer has
|
|
12
|
+
*
|
|
13
|
+
* The line between READING and ENFORCING already exists as `isGoverned`, which keys off a DECLARED
|
|
14
|
+
* type. This module does not move that line. It teaches the messages to speak about what lies
|
|
15
|
+
* outside it, and provides the one explicit act that moves a document across.
|
|
16
|
+
*
|
|
17
|
+
* Measured 2026-08-18: holmes-kit carries 155 such documents of 483, AX Coder 90 of 90. The id
|
|
18
|
+
* recovers the kind for 85–88% of them; the remainder are all `JOB-NNN`, a kind that no longer
|
|
19
|
+
* exists here.
|
|
20
|
+
*/
|
|
21
|
+
export type FormatState =
|
|
22
|
+
/** `type:` is declared and is a kind this tool has. Governed, and nothing here applies. */
|
|
23
|
+
{
|
|
24
|
+
kind: 'current';
|
|
25
|
+
type: SpecType;
|
|
26
|
+
}
|
|
27
|
+
/** No `type:`, but the id says what it is. Upgradable. */
|
|
28
|
+
| {
|
|
29
|
+
kind: 'legacy';
|
|
30
|
+
inferred: SpecType;
|
|
31
|
+
}
|
|
32
|
+
/** No usable kind at all — not something this tool governs. */
|
|
33
|
+
| {
|
|
34
|
+
kind: 'unsupported';
|
|
35
|
+
};
|
|
36
|
+
/** Frontmatter key that preserves the pre-upgrade status. Named once so writer and reader agree. */
|
|
37
|
+
export declare const LEGACY_STATUS_FIELD = "legacy_status";
|
|
38
|
+
/**
|
|
39
|
+
* Classify a document by FORMAT, not by validity.
|
|
40
|
+
*
|
|
41
|
+
* A DECLARED type always wins over the id. Inference exists to help a document that never said what
|
|
42
|
+
* it was — never to overrule a human who did. A declared type that this tool does not have is NOT
|
|
43
|
+
* `current`: declaring `type: JOB` must not smuggle an unknown kind past the governed boundary.
|
|
44
|
+
*/
|
|
45
|
+
export declare function formatStateOf(spec: {
|
|
46
|
+
id: string;
|
|
47
|
+
type?: SpecType;
|
|
48
|
+
}): FormatState;
|
|
49
|
+
/**
|
|
50
|
+
* Why this document cannot be used as a governed spec, and what to do about it — or `null` when the
|
|
51
|
+
* document is in current format and the caller's existing wording should stand.
|
|
52
|
+
*
|
|
53
|
+
* Returning `null` for `current` is what keeps this change from leaking into paths that were already
|
|
54
|
+
* correct: a parent of the wrong DECLARED kind was always reported accurately, and REQ-184 does not
|
|
55
|
+
* touch it.
|
|
56
|
+
*
|
|
57
|
+
* The two non-null cases must read differently. Telling the holder of a `JOB-001` to upgrade it
|
|
58
|
+
* would send them after a type that does not exist — the same "points nowhere" failure this REQ
|
|
59
|
+
* exists to close, merely relocated.
|
|
60
|
+
*/
|
|
61
|
+
export declare function legacyMessage(spec: {
|
|
62
|
+
id: string;
|
|
63
|
+
type?: SpecType;
|
|
64
|
+
}): string | null;
|
|
65
|
+
/**
|
|
66
|
+
* What an upgrade would change, or `null` when there is nothing to do.
|
|
67
|
+
*
|
|
68
|
+
* `null` for BOTH `current` and `unsupported`, for opposite reasons — already declared, or nothing
|
|
69
|
+
* to declare. The caller distinguishes them; this function only answers "is there a plan".
|
|
70
|
+
*
|
|
71
|
+
* The plan carries no status of its own on purpose. Upgrading declares a KIND; it does not decide
|
|
72
|
+
* whether the document is normative. 37 of this repository's legacy documents say `Approved`, and
|
|
73
|
+
* carrying that across would mint 37 approvals that never passed through the sealing act — no
|
|
74
|
+
* `approved_digest`, no `parent_digests`, no ledger entry. The old value is preserved as evidence
|
|
75
|
+
* and the document restarts at `draft`, from where the normal approval path can seal it properly.
|
|
76
|
+
*/
|
|
77
|
+
export declare function upgradePlan(spec: Spec): {
|
|
78
|
+
type: SpecType;
|
|
79
|
+
legacyStatus: string;
|
|
80
|
+
} | null;
|
|
81
|
+
/**
|
|
82
|
+
* The frontmatter an upgrade writes: everything that was there, plus the preserved status.
|
|
83
|
+
*
|
|
84
|
+
* Lives here rather than inline at the call site so the omission rule is exercisable. The store's
|
|
85
|
+
* parser defaults a missing status to `draft`, so a document with none cannot arrive through it —
|
|
86
|
+
* which is exactly why the rule would otherwise go untested and could be deleted unnoticed.
|
|
87
|
+
*
|
|
88
|
+
* The spread comes first so a document that somehow already carries `legacy_status` keeps it rather
|
|
89
|
+
* than having it rewritten from a status that is itself the result of an earlier upgrade.
|
|
90
|
+
*/
|
|
91
|
+
export declare function upgradedFrontmatter(spec: Spec, plan: {
|
|
92
|
+
legacyStatus: string;
|
|
93
|
+
}): Record<string, unknown>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Spec } from './spec-parser';
|
|
2
|
+
/**
|
|
3
|
+
* @implements A-SPEC-132
|
|
4
|
+
* The canonical content digest an approval SEALS. It covers what approving means — the spec's kind,
|
|
5
|
+
* its title, which parents it claims, and every section body — and deliberately excludes `status`
|
|
6
|
+
* and the seal fields themselves (a seal must not seal itself, and flipping status must not break
|
|
7
|
+
* the seal it accompanies). All other frontmatter is excluded too: owner/priority metadata churn is
|
|
8
|
+
* not a content change someone must re-approve.
|
|
9
|
+
*
|
|
10
|
+
* Canonicalization makes the digest platform- and formatting-stable: `dependsOn` is sorted, section
|
|
11
|
+
* keys are sorted, section bodies are LF-normalized and right-trimmed. Same content, same hex —
|
|
12
|
+
* reordering YAML keys or a CRLF checkout cannot fake a post-approval edit.
|
|
13
|
+
*/
|
|
14
|
+
export declare function specDigest(spec: Spec): string;
|
|
15
|
+
/** Frontmatter keys written only by an approval act (spec_approve / the one-time backfill). */
|
|
16
|
+
export declare const SEAL_FIELDS: readonly ["approved_digest", "parent_digests"];
|
|
17
|
+
/**
|
|
18
|
+
* @implements A-SPEC-132
|
|
19
|
+
* Test-only helper: return a copy of `spec` sealed at its current content, snapshotting each given
|
|
20
|
+
* parent's own seal. Fixtures that represent an APPROVED spec must now carry a seal to be valid — the
|
|
21
|
+
* validator enforces it — so tests build approved fixtures through this. Not for production writes:
|
|
22
|
+
* spec_approve is the only real sealer (it also validates and ledgers).
|
|
23
|
+
*/
|
|
24
|
+
export declare function sealForTest(spec: Spec, parents?: Spec[]): Spec;
|
|
25
|
+
/** The seal a spec carries, if any. */
|
|
26
|
+
export declare function sealOf(spec: Spec): {
|
|
27
|
+
approvedDigest?: string;
|
|
28
|
+
parentDigests: Record<string, string>;
|
|
29
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { SpecType, SpecStatus } from './spec-types';
|
|
2
|
+
export interface Spec {
|
|
3
|
+
id: string;
|
|
4
|
+
type: SpecType;
|
|
5
|
+
title: string;
|
|
6
|
+
status: SpecStatus;
|
|
7
|
+
dependsOn: string[];
|
|
8
|
+
frontmatter: Record<string, unknown>;
|
|
9
|
+
sections: Record<string, string>;
|
|
10
|
+
}
|
|
11
|
+
export declare function parseSpec(input: string): Spec;
|
|
12
|
+
export declare function serializeSpec(spec: Spec): string;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { SpecStore } from './spec-store';
|
|
2
|
+
/**
|
|
3
|
+
* The ONE conformance suite every `SpecStore` adapter must pass.
|
|
4
|
+
*
|
|
5
|
+
* Written entirely against the CONTRACT — no paths, no files, no knowledge of how anything is
|
|
6
|
+
* stored. ADR-014 recorded the limit measured on the ledger port: a storage-agnostic suite cannot
|
|
7
|
+
* see storage-specific defects, so each adapter still owes tests about its own medium. What belongs
|
|
8
|
+
* HERE is only what is meaningful for every adapter.
|
|
9
|
+
*/
|
|
10
|
+
export declare function specStoreConformance(name: string, make: () => SpecStore): void;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { Spec } from './spec-parser';
|
|
2
|
+
/**
|
|
3
|
+
* Thrown when `expectedVersion` no longer matches what the store holds.
|
|
4
|
+
*
|
|
5
|
+
* @implements A-SPEC-151
|
|
6
|
+
* Optimistic concurrency, which REQ-018's deprecation note named as the surviving fragment of that
|
|
7
|
+
* requirement: "스펙 수준 낙관적 동시성은 ports SpecStore expectedVersion(ADR-001)으로 생존".
|
|
8
|
+
*/
|
|
9
|
+
export declare class SpecVersionConflictError extends Error {
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* @implements A-SPEC-188
|
|
13
|
+
* A write would land on a path occupied by a file that is NOT this spec's own current file —
|
|
14
|
+
* typically an unparseable human note or broken document that list()/read() cannot see. Thrown
|
|
15
|
+
* before any mutation; the occupant is a human's to look at, never the store's to replace.
|
|
16
|
+
*/
|
|
17
|
+
export declare class TargetPathOccupiedError extends Error {
|
|
18
|
+
readonly occupiedPath: string;
|
|
19
|
+
constructor(message: string, occupiedPath: string);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The spec store as a PORT.
|
|
23
|
+
*
|
|
24
|
+
* @implements A-SPEC-151
|
|
25
|
+
* The version is DERIVED, never stored. A new frontmatter field would be an ADR-013 tier-① format
|
|
26
|
+
* change, dragging 302 existing specs into a migration and a read-compatibility problem; an opaque
|
|
27
|
+
* token the store computes at read time costs none of that. It is deliberately NOT `specDigest`,
|
|
28
|
+
* which excludes `status` and the seal fields — a draft→approved flip would be invisible to it. The
|
|
29
|
+
* two answer different questions: "is this the content that was approved" versus "has anyone touched
|
|
30
|
+
* this since I read it".
|
|
31
|
+
*
|
|
32
|
+
* DECLARED because they have callers and two adapters can be tested against them: `expectedVersion`
|
|
33
|
+
* and `external_id` (a spec mirrored from a tracker needs its foreign key).
|
|
34
|
+
*
|
|
35
|
+
* NOT DECLARED — locks, events, RBAC — although ADR-001 listed them. ADR-014, decided on the ledger
|
|
36
|
+
* port's measurement, forbids clauses with no caller that no two adapters can verify, and ADR-001's
|
|
37
|
+
* own Consequences named speculative port design as its downside. A wrong contract is more expensive
|
|
38
|
+
* than an absent one, since it is exactly what causes the core-wide rework ADR-001 set out to avoid.
|
|
39
|
+
* A lock would also duplicate what optimistic concurrency replaces. Widen when a caller appears.
|
|
40
|
+
*/
|
|
41
|
+
export interface SpecStore {
|
|
42
|
+
read(id: string): Promise<{
|
|
43
|
+
spec: Spec;
|
|
44
|
+
version: string;
|
|
45
|
+
} | null>;
|
|
46
|
+
write(spec: Spec, opts?: {
|
|
47
|
+
expectedVersion?: string;
|
|
48
|
+
}): Promise<{
|
|
49
|
+
version: string;
|
|
50
|
+
}>;
|
|
51
|
+
list(): Promise<Spec[]>;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* @implements A-SPEC-176
|
|
55
|
+
* Markdown files under the spec tree that the parser cannot turn into a spec.
|
|
56
|
+
*
|
|
57
|
+
* `list()` drops these via `if (spec.id)`, which is the right call for a gate — an unparseable file
|
|
58
|
+
* cannot be treated as approved. But dropping them silently means a user reads "that spec does not
|
|
59
|
+
* exist" while looking straight at the file. The verdict stays; the silence does not.
|
|
60
|
+
*/
|
|
61
|
+
export declare function unreadableSpecFiles(specsDir: string): string[];
|
|
62
|
+
export declare class LocalMarkdownRepository implements SpecStore {
|
|
63
|
+
private readonly root;
|
|
64
|
+
constructor(root: string);
|
|
65
|
+
/**
|
|
66
|
+
* @implements A-SPEC-169
|
|
67
|
+
* Where this store writes. Exposed so a handler can tell that the `root` it was given names a
|
|
68
|
+
* DIFFERENT project — measured 2026-08-08, a call with a temp root wrote into this repository
|
|
69
|
+
* instead, because the store is bound at start-up and the argument governed only the ledger.
|
|
70
|
+
*/
|
|
71
|
+
get specsRoot(): string;
|
|
72
|
+
private dirFor;
|
|
73
|
+
/** Version of the STORED representation — the serialized document, so a status flip changes it. */
|
|
74
|
+
private versionOf;
|
|
75
|
+
/**
|
|
76
|
+
* @implements A-SPEC-188
|
|
77
|
+
* The exact path a write of this spec would land on — PUBLIC so create-only callers can refuse
|
|
78
|
+
* when something already sits there. `list()` cannot answer that question: it drops files it
|
|
79
|
+
* cannot parse, and an unparseable document at the target path is precisely the one a create
|
|
80
|
+
* must not destroy (probed: a prose file at 01_req/REQ-777.md was silently overwritten by a
|
|
81
|
+
* guard that consulted list() alone — and the overwrite also emptied unreadableSpecFiles, so
|
|
82
|
+
* the doctor warning that would have surfaced the loss died with the document).
|
|
83
|
+
*/
|
|
84
|
+
targetPathFor(spec: Spec): string;
|
|
85
|
+
/**
|
|
86
|
+
* @implements A-SPEC-188
|
|
87
|
+
* Writes are SERIALIZED per store instance. The expectedVersion check and the file write are
|
|
88
|
+
* separated by an `await this.list()` (the orphan sweep), so two concurrent write() calls could
|
|
89
|
+
* both pass the check before either wrote — a check-then-act hole round-3 review demonstrated.
|
|
90
|
+
* The server is single-process; a promise chain closes the in-process window. Cross-process
|
|
91
|
+
* concurrency remains the caller's problem, as it was.
|
|
92
|
+
*/
|
|
93
|
+
private writeChain;
|
|
94
|
+
write(spec: Spec, opts?: {
|
|
95
|
+
expectedVersion?: string;
|
|
96
|
+
}): Promise<{
|
|
97
|
+
version: string;
|
|
98
|
+
}>;
|
|
99
|
+
private writeSerialized;
|
|
100
|
+
private findFilePath;
|
|
101
|
+
list(): Promise<Spec[]>;
|
|
102
|
+
read(id: string): Promise<{
|
|
103
|
+
spec: Spec;
|
|
104
|
+
version: string;
|
|
105
|
+
} | null>;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* The second adapter — the instrument that makes the contract testable.
|
|
109
|
+
*
|
|
110
|
+
* @implements A-SPEC-151
|
|
111
|
+
* Uses NO filesystem, deliberately: an in-memory store backed by a temp directory would be the file
|
|
112
|
+
* adapter in disguise and would prove nothing about coupling. The version is derived the same way in
|
|
113
|
+
* spirit — from the stored representation — but the representation here is an object, so it is
|
|
114
|
+
* hashed from a canonical serialization of the whole spec INCLUDING status, which is the property
|
|
115
|
+
* the conformance suite pins.
|
|
116
|
+
*/
|
|
117
|
+
export declare class MemorySpecStore implements SpecStore {
|
|
118
|
+
private readonly items;
|
|
119
|
+
private versionOf;
|
|
120
|
+
read(id: string): Promise<{
|
|
121
|
+
spec: Spec;
|
|
122
|
+
version: string;
|
|
123
|
+
} | null>;
|
|
124
|
+
write(spec: Spec, opts?: {
|
|
125
|
+
expectedVersion?: string;
|
|
126
|
+
}): Promise<{
|
|
127
|
+
version: string;
|
|
128
|
+
}>;
|
|
129
|
+
list(): Promise<Spec[]>;
|
|
130
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
export type SpecType = 'REQ' | 'H-SPEC' | 'A-SPEC' | 'C-SPEC' | 'T-SPEC';
|
|
2
|
+
export declare const SPEC_STATUSES: readonly ["draft", "review", "approved", "outdated"];
|
|
3
|
+
export type SpecStatus = (typeof SPEC_STATUSES)[number];
|
|
4
|
+
export interface SpecTypeDef {
|
|
5
|
+
type: SpecType;
|
|
6
|
+
idRegex: RegExp;
|
|
7
|
+
/**
|
|
8
|
+
* @implements A-SPEC-174
|
|
9
|
+
* A valid id of this type, shown to whoever `spec_create` just refused. Held here rather than
|
|
10
|
+
* derived from `idRegex`, because generating it would be a second statement of the same rule —
|
|
11
|
+
* and an example that its own rule rejects sends the reader back into the refusal they came from.
|
|
12
|
+
*/
|
|
13
|
+
example: string;
|
|
14
|
+
folder: string;
|
|
15
|
+
parents: SpecType[];
|
|
16
|
+
requiredFields: string[];
|
|
17
|
+
requiredSections: string[];
|
|
18
|
+
/**
|
|
19
|
+
* @implements A-SPEC-146
|
|
20
|
+
* Fields `spec_create` scaffolds but `validateSpec` does NOT require. The gap is deliberate and
|
|
21
|
+
* measured: a field that is required by the state predicate invalidates every already-approved
|
|
22
|
+
* spec that predates it — for `breaking_change` that was 38 ART-3 violations and a Stop hook
|
|
23
|
+
* blocking every turn. Such a duty belongs to the ACT that changes a spec, not to the document's
|
|
24
|
+
* static validity, while the stub keeps the field visible to whoever writes the next one.
|
|
25
|
+
*/
|
|
26
|
+
stubOnlyFields?: string[];
|
|
27
|
+
}
|
|
28
|
+
export declare const SPEC_ORDER: SpecType[];
|
|
29
|
+
/**
|
|
30
|
+
* Where a requirement came from.
|
|
31
|
+
*
|
|
32
|
+
* A REQ is the one artefact whose content cannot be recovered from the codebase — intent lives in
|
|
33
|
+
* people, tickets, incidents and decisions. That makes its provenance the weakest link in the whole
|
|
34
|
+
* chain: everything downstream is traceable to the REQ, and the REQ was traceable to a sentence of
|
|
35
|
+
* free text. Structuring it is what lets a future RequirementSourceAdapter / KnowledgeBaseAdapter
|
|
36
|
+
* (ports-and-adapters-design §2.1) fill this field mechanically instead of a human paraphrasing a
|
|
37
|
+
* wiki page, and what lets a changed upstream source mark its derived specs outdated.
|
|
38
|
+
*
|
|
39
|
+
* DELIBERATELY SHAPE-ONLY. The deterministic layer can check that a citation exists, names a kind it
|
|
40
|
+
* understands, points somewhere, and pins either a revision or a read date. It cannot check that the
|
|
41
|
+
* cited document says what the REQ claims — that stays a human review judgement, exactly as with the
|
|
42
|
+
* prose sections a reverse-engineered spec leaves as TODO.
|
|
43
|
+
*/
|
|
44
|
+
export declare const CITATION_KINDS: readonly ["user-request", "conversation", "plan", "adr", "architecture", "analysis", "issue", "wiki", "incident", "code-review", "regulation", "other"];
|
|
45
|
+
export type CitationKind = typeof CITATION_KINDS[number];
|
|
46
|
+
/**
|
|
47
|
+
* Kinds whose content can change after it was cited, and which therefore REQUIRE `rev`.
|
|
48
|
+
*
|
|
49
|
+
* The split is mutable vs immutable, not "has a version number". A wiki page, a ticket, an ADR or a
|
|
50
|
+
* plan file all keep changing under you, so a citation without a version pin cannot detect drift and
|
|
51
|
+
* the citation stops meaning anything. A conversation, a direct request, or an incident is fixed the
|
|
52
|
+
* moment it happened — `ref` plus `retrieved` already pins it completely, and demanding a `rev` there
|
|
53
|
+
* would only push an author to invent one, which is the fabrication this whole schema exists to stop.
|
|
54
|
+
*/
|
|
55
|
+
export declare const MUTABLE_CITATION_KINDS: readonly CitationKind[];
|
|
56
|
+
export interface Citation {
|
|
57
|
+
/** What sort of source this is. Drives which adapter can later resolve `ref`. */
|
|
58
|
+
kind: CitationKind;
|
|
59
|
+
/** A locator that identifies the source: URL, ticket id, document path, decision id. */
|
|
60
|
+
ref: string;
|
|
61
|
+
/**
|
|
62
|
+
* Pins WHAT was read. REQUIRED for every mutable source kind (see `MUTABLE_CITATION_KINDS`).
|
|
63
|
+
*
|
|
64
|
+
* The canonical form is a CONTENT DIGEST of the source as it was read — `sha256:<hex>`. That form
|
|
65
|
+
* is source-agnostic (any wiki page, ticket payload, or file can be hashed), needs no git, and is
|
|
66
|
+
* the only form that actually does this field's job: re-fetch, re-hash, compare, and an upstream
|
|
67
|
+
* edit becomes visible. It is also the idiom this project already uses everywhere else — the
|
|
68
|
+
* provenance chain's computeHash, the scan cache's hashOf, authorizationRef's token fingerprint.
|
|
69
|
+
*
|
|
70
|
+
* A source's own version handle is accepted too when it is content-derived or monotonic: a git
|
|
71
|
+
* sha, a Confluence page version, a JIRA `updated` timestamp, an HTTP ETag.
|
|
72
|
+
*
|
|
73
|
+
* A FRESHLY MINTED UNIQUE TOKEN IS NOT ACCEPTABLE. A random id assigned at citation time
|
|
74
|
+
* identifies the act of citing, not the state of the content: the wiki page changes and the token
|
|
75
|
+
* does not, so drift becomes undetectable and the citation quietly starts lying. The value must be
|
|
76
|
+
* derived FROM the source, never generated ALONGSIDE it.
|
|
77
|
+
*
|
|
78
|
+
* The string itself is stored opaquely — nothing parses it — but its provenance value depends
|
|
79
|
+
* entirely on being derived from content.
|
|
80
|
+
*/
|
|
81
|
+
rev?: string;
|
|
82
|
+
/**
|
|
83
|
+
* ISO date (YYYY-MM-DD) the source was read.
|
|
84
|
+
*
|
|
85
|
+
* The universal fallback. Every source can be dated even when it cannot be versioned, so this is
|
|
86
|
+
* what keeps the schema usable in a non-git environment, against a system with no revision API,
|
|
87
|
+
* or for a source that only ever existed in a conversation.
|
|
88
|
+
*/
|
|
89
|
+
retrieved?: string;
|
|
90
|
+
/** What was actually taken from the source. */
|
|
91
|
+
note?: string;
|
|
92
|
+
}
|
|
93
|
+
/** Placeholder a freshly created spec carries until its author supplies a real citation. */
|
|
94
|
+
export declare const CITATION_PLACEHOLDER = "TODO";
|
|
95
|
+
export declare const SPEC_TYPES: Record<SpecType, SpecTypeDef>;
|
|
96
|
+
/**
|
|
97
|
+
* The GOVERNED set is defined by the modern spec format: a spec is governed
|
|
98
|
+
* iff it carries a `type` field naming a known SpecType. This deliberately
|
|
99
|
+
* excludes (a) pre-`type` legacy specs retained only as archived history, and
|
|
100
|
+
* (b) the `06_job/JOB-*` runtime ledger, which is not a governed spec type.
|
|
101
|
+
* Validation and RTM integrity checks operate on the governed set — validating
|
|
102
|
+
* archived legacy or runtime-ledger entries is a category error (audit C1).
|
|
103
|
+
* Generic over `{ type?: SpecType }` to stay free of an import cycle with the
|
|
104
|
+
* parser (which imports SpecType from here).
|
|
105
|
+
*/
|
|
106
|
+
/**
|
|
107
|
+
* Own-property lookup into SPEC_TYPES. A bare `SPEC_TYPES[type]` inherits
|
|
108
|
+
* Object.prototype, so a spec with `type: constructor` / `__proto__` / etc.
|
|
109
|
+
* (raw unvalidated YAML frontmatter) would resolve to a prototype member and
|
|
110
|
+
* masquerade as a known type — then crash downstream on `def.idRegex`. This
|
|
111
|
+
* guard returns undefined for any non-own key.
|
|
112
|
+
*/
|
|
113
|
+
export declare function specTypeDef(type: string | undefined | null): SpecTypeDef | undefined;
|
|
114
|
+
export declare function isGoverned(spec: {
|
|
115
|
+
type?: SpecType;
|
|
116
|
+
}): boolean;
|
|
117
|
+
export declare function filterGoverned<T extends {
|
|
118
|
+
type?: SpecType;
|
|
119
|
+
}>(specs: T[]): T[];
|
|
120
|
+
/**
|
|
121
|
+
* @implements A-SPEC-174
|
|
122
|
+
* The value `spec_create` writes into a required field it cannot answer.
|
|
123
|
+
*
|
|
124
|
+
* Named because two places must agree on it: the handler that writes it, and the store that must NOT
|
|
125
|
+
* treat it as a directory name. Measured 2026-08-12 — a stubbed `req_type` produced a literal
|
|
126
|
+
* `.ax/specs/02_h-spec/TODO/` in the user's repository.
|
|
127
|
+
*/
|
|
128
|
+
export declare const FIELD_PLACEHOLDER = "TODO";
|
|
129
|
+
/**
|
|
130
|
+
* @implements A-SPEC-174
|
|
131
|
+
* Which spec type an id belongs to, or null if no type claims it.
|
|
132
|
+
*
|
|
133
|
+
* Judged by the same `idRegex` the approval-time validator uses, not by a prefix comparison. A second
|
|
134
|
+
* statement of the rule drifts, and the failure it produces is either "created but never approvable"
|
|
135
|
+
* (which is the defect this closes) or the worse inverse — refused at creation, accepted at approval.
|
|
136
|
+
*
|
|
137
|
+
* This is what lets a parent be checked WITHOUT resolving it: a parent that does not exist yet is a
|
|
138
|
+
* legitimate state an author reaches by creating the child first, while a parent of the wrong type is
|
|
139
|
+
* wrong forever.
|
|
140
|
+
*/
|
|
141
|
+
export declare function specTypeOfId(id: string): SpecType | null;
|
|
142
|
+
/**
|
|
143
|
+
* @implements A-SPEC-174
|
|
144
|
+
* The parent-kind rule as prose, GENERATED from the relation itself.
|
|
145
|
+
*
|
|
146
|
+
* Written by hand this drifted the moment a clause was edited, and prose assertions could not tell
|
|
147
|
+
* the difference — a test looking for "H-SPEC ... REQ" still matched a sentence that had lost the
|
|
148
|
+
* H-SPEC rule entirely. Generating it removes the second copy instead of testing around it.
|
|
149
|
+
*/
|
|
150
|
+
export declare function parentRuleText(): string;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Spec } from './spec-parser';
|
|
2
|
+
export interface Finding {
|
|
3
|
+
level: 'error' | 'warn';
|
|
4
|
+
code: string;
|
|
5
|
+
message: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ValidationResult {
|
|
8
|
+
ok: boolean;
|
|
9
|
+
findings: Finding[];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* The canonical `rev` value for a citation: a content digest of the source as it was read.
|
|
13
|
+
*
|
|
14
|
+
* Deliberately computed from bytes with node:crypto and nothing else — no git, no network, no
|
|
15
|
+
* tracker API. A purely local harness with a wiki page saved to disk, a repository with no VCS, and
|
|
16
|
+
* an air-gapped machine all produce the same value for the same content, which is what makes this
|
|
17
|
+
* the one `rev` scheme that is always available. Truncated to 16 hex chars: this detects drift, it
|
|
18
|
+
* does not resist a motivated forger, and the surrounding provenance chain is what covers that.
|
|
19
|
+
*/
|
|
20
|
+
export declare function citationDigest(content: string | Uint8Array): string;
|
|
21
|
+
export interface PinResult {
|
|
22
|
+
/** The citation list with newly computed digests filled in. Unchanged entries are preserved. */
|
|
23
|
+
citations: unknown[];
|
|
24
|
+
/** `ref`s that gained a digest. */
|
|
25
|
+
pinned: string[];
|
|
26
|
+
/** Findings: drift on already-pinned entries, and refs that could not be resolved. */
|
|
27
|
+
findings: Finding[];
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Fill in `rev` for citations whose `ref` resolves to readable content and that have none yet —
|
|
31
|
+
* the authoring counterpart of verifyCitationDigests, so nobody has to hash a file by hand.
|
|
32
|
+
*
|
|
33
|
+
* NEVER OVERWRITES AN EXISTING rev. That restriction is the whole safety property: re-pinning a
|
|
34
|
+
* stale citation would silently replace the evidence of drift with a fresh-looking digest, turning
|
|
35
|
+
* the one mechanism that detects upstream change into the mechanism that hides it. When an existing
|
|
36
|
+
* digest no longer matches, this reports drift and leaves the stored value alone — resolving it is a
|
|
37
|
+
* human decision (re-read the source and confirm the requirement still holds), not a rewrite.
|
|
38
|
+
*
|
|
39
|
+
* Pinning asserts "this content is what I am citing", so it only ever runs when explicitly invoked.
|
|
40
|
+
*/
|
|
41
|
+
export declare function pinCitations(citations: unknown, read: (ref: string) => string | Uint8Array | null): PinResult;
|
|
42
|
+
/**
|
|
43
|
+
* Re-derive each citation's digest and report the ones that no longer match — the step that turns a
|
|
44
|
+
* stored `rev` string from decoration into actual drift detection.
|
|
45
|
+
*
|
|
46
|
+
* `read` resolves a `ref` to the source's current bytes, returning null when it cannot be fetched
|
|
47
|
+
* (an external ticket with no adapter configured, a deleted file). Unfetchable is NOT a failure: the
|
|
48
|
+
* point is to flag sources that DID change, not to demand that every citation be resolvable in every
|
|
49
|
+
* environment. Injected rather than importing fs so the rule stays pure and testable.
|
|
50
|
+
*/
|
|
51
|
+
export declare function verifyCitationDigests(citations: unknown, read: (ref: string) => string | Uint8Array | null): Finding[];
|
|
52
|
+
/**
|
|
53
|
+
* Validate a REQ's `source` as a list of structured citations.
|
|
54
|
+
*
|
|
55
|
+
* Two tiers, mirroring how the T-SPEC coverage gate works. SHAPE problems are always errors — a
|
|
56
|
+
* citation the machine cannot parse is worse than none, because it looks like provenance. COMPLETENESS
|
|
57
|
+
* problems (a placeholder ref, no revision and no read date) are warnings while the REQ is still being
|
|
58
|
+
* written and become errors on approval, so a spec can be created and iterated without fighting the
|
|
59
|
+
* validator, but cannot be APPROVED while its provenance is a stub.
|
|
60
|
+
*
|
|
61
|
+
* Exported for direct testing: this is the rule set that decides whether a requirement's origin is
|
|
62
|
+
* auditable, and it should be pinned independently of the surrounding spec checks.
|
|
63
|
+
*/
|
|
64
|
+
export declare function validateCitations(value: unknown, approved: boolean): Finding[];
|
|
65
|
+
export declare function validateSpec(spec: Spec, resolve: (id: string) => Spec | null): ValidationResult;
|
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"//": "@implements A-SPEC-209",
|
|
3
3
|
"name": "@holmes-lab/holmes-kit",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.8",
|
|
5
5
|
"description": "Holmes-Kit — deterministic Agentic Software Engineering (ASE) harness with causal traceability (spec chain + D-CPG + RTM + phase guardrail)",
|
|
6
6
|
"main": "dist/holmes/mcp/server.js",
|
|
7
|
+
"types": "dist/holmes/mcp/server.d.ts",
|
|
7
8
|
"bin": {
|
|
8
9
|
"holmes-kit": "bin/holmes-kit.js",
|
|
9
10
|
"holmes-mcp": "bin/holmes-mcp.js"
|
|
@@ -68,11 +69,11 @@
|
|
|
68
69
|
},
|
|
69
70
|
"repository": {
|
|
70
71
|
"type": "git",
|
|
71
|
-
"url": "git+https://github.com/
|
|
72
|
+
"url": "git+https://github.com/holmes-kit/holmes-kit.git"
|
|
72
73
|
},
|
|
73
|
-
"homepage": "https://github.com/
|
|
74
|
+
"homepage": "https://github.com/holmes-kit/holmes-kit#readme",
|
|
74
75
|
"bugs": {
|
|
75
|
-
"url": "https://github.com/
|
|
76
|
+
"url": "https://github.com/holmes-kit/holmes-kit/issues"
|
|
76
77
|
},
|
|
77
78
|
"publishConfig": {
|
|
78
79
|
"access": "public"
|