wingfoil 0.2.1
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/LICENSE +21 -0
- package/README.md +334 -0
- package/dist/cli/error.d.ts +11 -0
- package/dist/cli/error.d.ts.map +1 -0
- package/dist/cli/error.js +27 -0
- package/dist/cli/error.js.map +1 -0
- package/dist/cli/exit.d.ts +19 -0
- package/dist/cli/exit.d.ts.map +1 -0
- package/dist/cli/exit.js +14 -0
- package/dist/cli/exit.js.map +1 -0
- package/dist/cli/index.d.ts +28 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +36 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init-command.d.ts +34 -0
- package/dist/cli/init-command.d.ts.map +1 -0
- package/dist/cli/init-command.js +100 -0
- package/dist/cli/init-command.js.map +1 -0
- package/dist/cli/mcp-command.d.ts +24 -0
- package/dist/cli/mcp-command.d.ts.map +1 -0
- package/dist/cli/mcp-command.js +42 -0
- package/dist/cli/mcp-command.js.map +1 -0
- package/dist/cli/output.d.ts +13 -0
- package/dist/cli/output.d.ts.map +1 -0
- package/dist/cli/output.js +28 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/cli/program.d.ts +12 -0
- package/dist/cli/program.d.ts.map +1 -0
- package/dist/cli/program.js +279 -0
- package/dist/cli/program.js.map +1 -0
- package/dist/cli/registrar.d.ts +69 -0
- package/dist/cli/registrar.d.ts.map +1 -0
- package/dist/cli/registrar.js +82 -0
- package/dist/cli/registrar.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +45 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/approval-authority.d.ts +83 -0
- package/dist/core/approval-authority.d.ts.map +1 -0
- package/dist/core/approval-authority.js +112 -0
- package/dist/core/approval-authority.js.map +1 -0
- package/dist/core/builtin-asset.d.ts +71 -0
- package/dist/core/builtin-asset.d.ts.map +1 -0
- package/dist/core/builtin-asset.js +137 -0
- package/dist/core/builtin-asset.js.map +1 -0
- package/dist/core/builtin-integrity.d.ts +32 -0
- package/dist/core/builtin-integrity.d.ts.map +1 -0
- package/dist/core/builtin-integrity.js +148 -0
- package/dist/core/builtin-integrity.js.map +1 -0
- package/dist/core/confinement.d.ts +57 -0
- package/dist/core/confinement.d.ts.map +1 -0
- package/dist/core/confinement.js +108 -0
- package/dist/core/confinement.js.map +1 -0
- package/dist/core/context.d.ts +187 -0
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/context.js +192 -0
- package/dist/core/context.js.map +1 -0
- package/dist/core/directive-assign.d.ts +131 -0
- package/dist/core/directive-assign.d.ts.map +1 -0
- package/dist/core/directive-assign.js +352 -0
- package/dist/core/directive-assign.js.map +1 -0
- package/dist/core/directives-list.d.ts +80 -0
- package/dist/core/directives-list.d.ts.map +1 -0
- package/dist/core/directives-list.js +148 -0
- package/dist/core/directives-list.js.map +1 -0
- package/dist/core/exit-code.d.ts +85 -0
- package/dist/core/exit-code.d.ts.map +1 -0
- package/dist/core/exit-code.js +163 -0
- package/dist/core/exit-code.js.map +1 -0
- package/dist/core/git-identity.d.ts +32 -0
- package/dist/core/git-identity.d.ts.map +1 -0
- package/dist/core/git-identity.js +72 -0
- package/dist/core/git-identity.js.map +1 -0
- package/dist/core/index.d.ts +388 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +1331 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/init.d.ts +78 -0
- package/dist/core/init.d.ts.map +1 -0
- package/dist/core/init.js +167 -0
- package/dist/core/init.js.map +1 -0
- package/dist/core/loaders.d.ts +174 -0
- package/dist/core/loaders.d.ts.map +1 -0
- package/dist/core/loaders.js +364 -0
- package/dist/core/loaders.js.map +1 -0
- package/dist/core/memory-add-type.d.ts +43 -0
- package/dist/core/memory-add-type.d.ts.map +1 -0
- package/dist/core/memory-add-type.js +151 -0
- package/dist/core/memory-add-type.js.map +1 -0
- package/dist/core/memory-transition.d.ts +111 -0
- package/dist/core/memory-transition.d.ts.map +1 -0
- package/dist/core/memory-transition.js +269 -0
- package/dist/core/memory-transition.js.map +1 -0
- package/dist/core/registry.d.ts +189 -0
- package/dist/core/registry.d.ts.map +1 -0
- package/dist/core/registry.js +66 -0
- package/dist/core/registry.js.map +1 -0
- package/dist/core/relevance.d.ts +86 -0
- package/dist/core/relevance.d.ts.map +1 -0
- package/dist/core/relevance.js +258 -0
- package/dist/core/relevance.js.map +1 -0
- package/dist/core/require-reason.d.ts +27 -0
- package/dist/core/require-reason.d.ts.map +1 -0
- package/dist/core/require-reason.js +95 -0
- package/dist/core/require-reason.js.map +1 -0
- package/dist/core/types.d.ts +45 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +22 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/usage-error.d.ts +20 -0
- package/dist/core/usage-error.d.ts.map +1 -0
- package/dist/core/usage-error.js +29 -0
- package/dist/core/usage-error.js.map +1 -0
- package/dist/core/write-guard.d.ts +99 -0
- package/dist/core/write-guard.d.ts.map +1 -0
- package/dist/core/write-guard.js +247 -0
- package/dist/core/write-guard.js.map +1 -0
- package/dist/directives/create.d.ts +47 -0
- package/dist/directives/create.d.ts.map +1 -0
- package/dist/directives/create.js +82 -0
- package/dist/directives/create.js.map +1 -0
- package/dist/directives/index.d.ts +20 -0
- package/dist/directives/index.d.ts.map +1 -0
- package/dist/directives/index.js +32 -0
- package/dist/directives/index.js.map +1 -0
- package/dist/directives/roles-edit.d.ts +53 -0
- package/dist/directives/roles-edit.d.ts.map +1 -0
- package/dist/directives/roles-edit.js +286 -0
- package/dist/directives/roles-edit.js.map +1 -0
- package/dist/directives/schema.d.ts +65 -0
- package/dist/directives/schema.d.ts.map +1 -0
- package/dist/directives/schema.js +68 -0
- package/dist/directives/schema.js.map +1 -0
- package/dist/dna/edit.d.ts +61 -0
- package/dist/dna/edit.d.ts.map +1 -0
- package/dist/dna/edit.js +429 -0
- package/dist/dna/edit.js.map +1 -0
- package/dist/dna/index.d.ts +24 -0
- package/dist/dna/index.d.ts.map +1 -0
- package/dist/dna/index.js +59 -0
- package/dist/dna/index.js.map +1 -0
- package/dist/dna/mutate.d.ts +74 -0
- package/dist/dna/mutate.d.ts.map +1 -0
- package/dist/dna/mutate.js +318 -0
- package/dist/dna/mutate.js.map +1 -0
- package/dist/dna/path.d.ts +175 -0
- package/dist/dna/path.d.ts.map +1 -0
- package/dist/dna/path.js +289 -0
- package/dist/dna/path.js.map +1 -0
- package/dist/dna/roles.d.ts +57 -0
- package/dist/dna/roles.d.ts.map +1 -0
- package/dist/dna/roles.js +50 -0
- package/dist/dna/roles.js.map +1 -0
- package/dist/dna/schema.d.ts +172 -0
- package/dist/dna/schema.d.ts.map +1 -0
- package/dist/dna/schema.js +182 -0
- package/dist/dna/schema.js.map +1 -0
- package/dist/dna/set.d.ts +145 -0
- package/dist/dna/set.d.ts.map +1 -0
- package/dist/dna/set.js +428 -0
- package/dist/dna/set.js.map +1 -0
- package/dist/mcp/dna-resource.d.ts +16 -0
- package/dist/mcp/dna-resource.d.ts.map +1 -0
- package/dist/mcp/dna-resource.js +56 -0
- package/dist/mcp/dna-resource.js.map +1 -0
- package/dist/mcp/index.d.ts +49 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +60 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/memory-resource.d.ts +17 -0
- package/dist/mcp/memory-resource.d.ts.map +1 -0
- package/dist/mcp/memory-resource.js +133 -0
- package/dist/mcp/memory-resource.js.map +1 -0
- package/dist/mcp/prompt.d.ts +48 -0
- package/dist/mcp/prompt.d.ts.map +1 -0
- package/dist/mcp/prompt.js +176 -0
- package/dist/mcp/prompt.js.map +1 -0
- package/dist/mcp/read-only.d.ts +42 -0
- package/dist/mcp/read-only.d.ts.map +1 -0
- package/dist/mcp/read-only.js +93 -0
- package/dist/mcp/read-only.js.map +1 -0
- package/dist/mcp/registrar.d.ts +54 -0
- package/dist/mcp/registrar.d.ts.map +1 -0
- package/dist/mcp/registrar.js +72 -0
- package/dist/mcp/registrar.js.map +1 -0
- package/dist/mcp/server.d.ts +58 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +64 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/workflow-resource.d.ts +17 -0
- package/dist/mcp/workflow-resource.d.ts.map +1 -0
- package/dist/mcp/workflow-resource.js +63 -0
- package/dist/mcp/workflow-resource.js.map +1 -0
- package/dist/memory/add.d.ts +50 -0
- package/dist/memory/add.d.ts.map +1 -0
- package/dist/memory/add.js +131 -0
- package/dist/memory/add.js.map +1 -0
- package/dist/memory/audit.d.ts +177 -0
- package/dist/memory/audit.d.ts.map +1 -0
- package/dist/memory/audit.js +287 -0
- package/dist/memory/audit.js.map +1 -0
- package/dist/memory/commit-message.d.ts +146 -0
- package/dist/memory/commit-message.d.ts.map +1 -0
- package/dist/memory/commit-message.js +203 -0
- package/dist/memory/commit-message.js.map +1 -0
- package/dist/memory/entry.d.ts +56 -0
- package/dist/memory/entry.d.ts.map +1 -0
- package/dist/memory/entry.js +28 -0
- package/dist/memory/entry.js.map +1 -0
- package/dist/memory/frontmatter-edit.d.ts +68 -0
- package/dist/memory/frontmatter-edit.d.ts.map +1 -0
- package/dist/memory/frontmatter-edit.js +333 -0
- package/dist/memory/frontmatter-edit.js.map +1 -0
- package/dist/memory/git-log.d.ts +32 -0
- package/dist/memory/git-log.d.ts.map +1 -0
- package/dist/memory/git-log.js +100 -0
- package/dist/memory/git-log.js.map +1 -0
- package/dist/memory/history.d.ts +142 -0
- package/dist/memory/history.d.ts.map +1 -0
- package/dist/memory/history.js +250 -0
- package/dist/memory/history.js.map +1 -0
- package/dist/memory/index.d.ts +58 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +107 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/memory/query.d.ts +150 -0
- package/dist/memory/query.d.ts.map +1 -0
- package/dist/memory/query.js +307 -0
- package/dist/memory/query.js.map +1 -0
- package/dist/memory/reject.d.ts +11 -0
- package/dist/memory/reject.d.ts.map +1 -0
- package/dist/memory/reject.js +32 -0
- package/dist/memory/reject.js.map +1 -0
- package/dist/memory/schema.d.ts +104 -0
- package/dist/memory/schema.d.ts.map +1 -0
- package/dist/memory/schema.js +161 -0
- package/dist/memory/schema.js.map +1 -0
- package/dist/memory/state-machine.d.ts +166 -0
- package/dist/memory/state-machine.d.ts.map +1 -0
- package/dist/memory/state-machine.js +431 -0
- package/dist/memory/state-machine.js.map +1 -0
- package/dist/memory/submit.d.ts +11 -0
- package/dist/memory/submit.d.ts.map +1 -0
- package/dist/memory/submit.js +44 -0
- package/dist/memory/submit.js.map +1 -0
- package/dist/storage/builtin-directives.d.ts +54 -0
- package/dist/storage/builtin-directives.d.ts.map +1 -0
- package/dist/storage/builtin-directives.js +152 -0
- package/dist/storage/builtin-directives.js.map +1 -0
- package/dist/storage/commit.d.ts +92 -0
- package/dist/storage/commit.d.ts.map +1 -0
- package/dist/storage/commit.js +201 -0
- package/dist/storage/commit.js.map +1 -0
- package/dist/storage/confinement.d.ts +93 -0
- package/dist/storage/confinement.d.ts.map +1 -0
- package/dist/storage/confinement.js +164 -0
- package/dist/storage/confinement.js.map +1 -0
- package/dist/storage/document.d.ts +27 -0
- package/dist/storage/document.d.ts.map +1 -0
- package/dist/storage/document.js +52 -0
- package/dist/storage/document.js.map +1 -0
- package/dist/storage/errors.d.ts +36 -0
- package/dist/storage/errors.d.ts.map +1 -0
- package/dist/storage/errors.js +46 -0
- package/dist/storage/errors.js.map +1 -0
- package/dist/storage/frontmatter.d.ts +27 -0
- package/dist/storage/frontmatter.d.ts.map +1 -0
- package/dist/storage/frontmatter.js +37 -0
- package/dist/storage/frontmatter.js.map +1 -0
- package/dist/storage/git-root.d.ts +17 -0
- package/dist/storage/git-root.d.ts.map +1 -0
- package/dist/storage/git-root.js +49 -0
- package/dist/storage/git-root.js.map +1 -0
- package/dist/storage/index.d.ts +29 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +66 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/init-state.d.ts +9 -0
- package/dist/storage/init-state.d.ts.map +1 -0
- package/dist/storage/init-state.js +24 -0
- package/dist/storage/init-state.js.map +1 -0
- package/dist/storage/layout.d.ts +51 -0
- package/dist/storage/layout.d.ts.map +1 -0
- package/dist/storage/layout.js +88 -0
- package/dist/storage/layout.js.map +1 -0
- package/dist/storage/memory-path.d.ts +72 -0
- package/dist/storage/memory-path.d.ts.map +1 -0
- package/dist/storage/memory-path.js +127 -0
- package/dist/storage/memory-path.js.map +1 -0
- package/dist/storage/snapshot.d.ts +16 -0
- package/dist/storage/snapshot.d.ts.map +1 -0
- package/dist/storage/snapshot.js +72 -0
- package/dist/storage/snapshot.js.map +1 -0
- package/dist/storage/templates.d.ts +88 -0
- package/dist/storage/templates.d.ts.map +1 -0
- package/dist/storage/templates.js +422 -0
- package/dist/storage/templates.js.map +1 -0
- package/dist/validation/error-mapper.d.ts +16 -0
- package/dist/validation/error-mapper.d.ts.map +1 -0
- package/dist/validation/error-mapper.js +18 -0
- package/dist/validation/error-mapper.js.map +1 -0
- package/dist/validation/errors.d.ts +69 -0
- package/dist/validation/errors.d.ts.map +1 -0
- package/dist/validation/errors.js +69 -0
- package/dist/validation/errors.js.map +1 -0
- package/dist/validation/id.d.ts +16 -0
- package/dist/validation/id.d.ts.map +1 -0
- package/dist/validation/id.js +177 -0
- package/dist/validation/id.js.map +1 -0
- package/dist/validation/index.d.ts +18 -0
- package/dist/validation/index.d.ts.map +1 -0
- package/dist/validation/index.js +37 -0
- package/dist/validation/index.js.map +1 -0
- package/dist/validation/secret-scan.d.ts +129 -0
- package/dist/validation/secret-scan.d.ts.map +1 -0
- package/dist/validation/secret-scan.js +367 -0
- package/dist/validation/secret-scan.js.map +1 -0
- package/dist/validation/two-pass.d.ts +24 -0
- package/dist/validation/two-pass.d.ts.map +1 -0
- package/dist/validation/two-pass.js +24 -0
- package/dist/validation/two-pass.js.map +1 -0
- package/dist/validation/warning.d.ts +31 -0
- package/dist/validation/warning.d.ts.map +1 -0
- package/dist/validation/warning.js +81 -0
- package/dist/validation/warning.js.map +1 -0
- package/dist/validation/yaml.d.ts +9 -0
- package/dist/validation/yaml.d.ts.map +1 -0
- package/dist/validation/yaml.js +30 -0
- package/dist/validation/yaml.js.map +1 -0
- package/dist/workflow/index.d.ts +10 -0
- package/dist/workflow/index.d.ts.map +1 -0
- package/dist/workflow/index.js +16 -0
- package/dist/workflow/index.js.map +1 -0
- package/dist/workflow/schema.d.ts +89 -0
- package/dist/workflow/schema.d.ts.map +1 -0
- package/dist/workflow/schema.js +73 -0
- package/dist/workflow/schema.js.map +1 -0
- package/package.json +78 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import type { DocumentScope, MemoryYaml, TransitionOp } from '../memory';
|
|
2
|
+
import { type CoreResult } from './types';
|
|
3
|
+
/** A document located and cleared for one transition — everything the verb needs to finish it. */
|
|
4
|
+
export interface PreparedMemoryTransition {
|
|
5
|
+
/**
|
|
6
|
+
* The `memory.yaml` this transition was resolved against — **the one committed at `HEAD`**, never
|
|
7
|
+
* the working tree's (task-091, `bug-081`). It rides the result rather than being re-loaded by the
|
|
8
|
+
* verb so that a single command cannot decide its transition from one copy and its follow-up facts
|
|
9
|
+
* (`memory submit`'s `template.frontmatter.required`) from another.
|
|
10
|
+
*/
|
|
11
|
+
readonly memoryYaml: MemoryYaml;
|
|
12
|
+
readonly id: string;
|
|
13
|
+
/** The document's declared frontmatter `type` (a registered `memory.yaml` type). */
|
|
14
|
+
readonly type: string;
|
|
15
|
+
/** Root-relative POSIX path of the document. */
|
|
16
|
+
readonly path: string;
|
|
17
|
+
/** The document's parsed frontmatter as read (not schema-validated). */
|
|
18
|
+
readonly frontmatter: Readonly<Record<string, unknown>>;
|
|
19
|
+
/** The full current file content, the base for the verb's edit. */
|
|
20
|
+
readonly content: string;
|
|
21
|
+
readonly from: string;
|
|
22
|
+
readonly to: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Locate document `id` and resolve verb `op` on it, **against the `memory.yaml` committed at
|
|
26
|
+
* `HEAD`** (`loadMemoryYamlAtHead`).
|
|
27
|
+
*
|
|
28
|
+
* The baseline is the point (task-091, `bug-081`, `dl-080` option (B)). This function takes no
|
|
29
|
+
* `MemoryYaml`: a caller cannot hand it a working-tree machine even by accident, which is what makes
|
|
30
|
+
* the committed baseline a property of the read itself rather than of a precondition someone must
|
|
31
|
+
* remember to run. An uncommitted edit to a type's `sequence` used to decide both the transition a
|
|
32
|
+
* verb performed and the `status` it committed — through `memory submit`, which requires no
|
|
33
|
+
* authority — and stranded the element in a state the committed machine rejects.
|
|
34
|
+
*
|
|
35
|
+
* Refusals, all returned (never thrown) and all exit `1`:
|
|
36
|
+
*
|
|
37
|
+
* - `HEAD` holds no `memory.yaml` → `VALIDATION`, fail-closed: with no committed machine there is no
|
|
38
|
+
* `from → to` to record, and the only fail-open available is the working tree, which is the defect.
|
|
39
|
+
* `wingfoil init` commits the scaffold, so no legitimate flow reaches this;
|
|
40
|
+
* - the committed `memory.yaml` does not parse or validate → `VALIDATION`, same reasoning;
|
|
41
|
+
* - no document carries that frontmatter `id` → `NOT_FOUND` `document not found: <id>` (P1.6 sc.3);
|
|
42
|
+
* - its `type` is not registered → `NOT_FOUND` with `memory add`'s unknown-type message;
|
|
43
|
+
* - its `status` is not a state of the type → `VALIDATION` `invalid state '<s>' for type '<t>'`
|
|
44
|
+
* (task-036's `validateFrontmatterState`);
|
|
45
|
+
* - the verb is illegal from that state → `INVALID_TRANSITION` with the `dl-032` contract message
|
|
46
|
+
* (`resolveTypeTransition`), the engine's explanation in `details.issues[0].detail`.
|
|
47
|
+
*
|
|
48
|
+
* The last four keep their pinned messages verbatim as the first sentence;
|
|
49
|
+
* {@link uncommittedMachineNote} may append a second one.
|
|
50
|
+
*/
|
|
51
|
+
export declare function prepareMemoryTransition(root: string, id: string, op: TransitionOp): CoreResult<PreparedMemoryTransition>;
|
|
52
|
+
/**
|
|
53
|
+
* Verify what the new commit **contains**, by diffing it against its own parent (task-088, AC3).
|
|
54
|
+
*
|
|
55
|
+
* This is the half that makes the guard above checkable rather than hopeful. Reading the file on disk
|
|
56
|
+
* answers "does the document now say `approved`?" — true no matter what else rode along; `HEAD~1..HEAD`
|
|
57
|
+
* is not. Same shape as `task-080`'s lockfile guard: assert the diff, not the end state. The parent is
|
|
58
|
+
* `<sha>^`, or git's empty tree for a root commit, so the check is total rather than conditional.
|
|
59
|
+
*
|
|
60
|
+
* It is an **alarm, not a rollback**: it can only run once the commit exists, and rewriting history
|
|
61
|
+
* behind the user's back is a worse failure than reporting one (`dl-035` points the same way). With
|
|
62
|
+
* {@link requireUnmodifiedDocument} in place it should be unreachable, which is the point.
|
|
63
|
+
*/
|
|
64
|
+
export declare function verifyCommittedScope(root: string, sha: string, path: string, expected: Readonly<Record<string, string | undefined>>, scope: DocumentScope): string[];
|
|
65
|
+
/**
|
|
66
|
+
* Write `content` over the prepared document and commit exactly that one path with `message`,
|
|
67
|
+
* returning the new commit's sha (`commitPaths`, task-018; scoped to that path even when other changes
|
|
68
|
+
* are staged, bug-027). Callers must have run the git-identity pre-flight (REQ-SEC-01) and every refusal
|
|
69
|
+
* check first; this step is the only write.
|
|
70
|
+
*
|
|
71
|
+
* Four checks bound what reaches the repository, in this order — they are not redundant, they catch
|
|
72
|
+
* different defects:
|
|
73
|
+
*
|
|
74
|
+
* 1. **The document is the project's to write** ({@link requireConfinedWriteTarget}, REQ-SEC-06) —
|
|
75
|
+
* catches a wrong *place*, in the two shapes a write can meet it. A type directory that is a
|
|
76
|
+
* symlink out of the project puts the document outside the root (`bug-117`), where `writeDocument`
|
|
77
|
+
* rewrites a file the repository does not own and `git add` then fails with its own text; and a
|
|
78
|
+
* **document** that is itself a symlink does the same with no symlinked directory anywhere,
|
|
79
|
+
* because `writeFileSync` follows the link that `unlinkSync` would merely remove (`bug-120` D2,
|
|
80
|
+
* measured on all four verbs). `bug-117` reports the first for `memory add`, whose target is
|
|
81
|
+
* resolved by `resolveConfinedMemoryPath`; these four verbs never ask that resolver anything —
|
|
82
|
+
* they locate an existing document — so both questions are asked here, about the path they are
|
|
83
|
+
* about to write. It runs **first**, and for every `scope`: the question "is this file ours at
|
|
84
|
+
* all" precedes every question about its content, and check 2 cannot stand in for it (`git status
|
|
85
|
+
* --porcelain` reports a path beyond a symbolic link as clean, which is `bug-118`).
|
|
86
|
+
* 2. **The working tree is unmodified** ({@link requireUnmodifiedDocument}, `declared-fields-only`
|
|
87
|
+
* only) — catches a wrong *baseline*: content that was already on disk before the verb ran
|
|
88
|
+
* (`bug-076`).
|
|
89
|
+
* 3. **The rendering is in scope** ({@link verifyDocumentEdit} against the prepared document) —
|
|
90
|
+
* catches a defect in the *editor*: `status` must be the prepared target, every field in `expected`
|
|
91
|
+
* must have its value (`undefined` = absent), and no other field and no byte of the body may have
|
|
92
|
+
* moved (the `bug-041` class). Runs under `declared-fields-only` for **every** verb, `submit`
|
|
93
|
+
* included: `renderSubmitDocument` owns `status` and `rejection_reason` alone, and the content
|
|
94
|
+
* `submit` carries is already in the prepared document it is compared against.
|
|
95
|
+
* 4. **The commit contains only the declared change** ({@link verifyCommittedScope} against
|
|
96
|
+
* `HEAD~1`) — the post-condition proper, and the only one that measures the artefact of record.
|
|
97
|
+
*
|
|
98
|
+
* Checks 1–3 report a `VALIDATION` error (exit 1) with nothing written or committed. Check 4 can
|
|
99
|
+
* only report; see {@link verifyCommittedScope}.
|
|
100
|
+
*
|
|
101
|
+
* Check 1 resolves on the **working tree**, not at `HEAD`, which departs from `command-baseline`'s
|
|
102
|
+
* rule for a gating read. The argument is `task-102`'s, recorded in
|
|
103
|
+
* `dl-086-a-guard-over-a-filesystem-effect-resolves-on-the-filesystem` (`in-discussion`) and cited
|
|
104
|
+
* rather than re-made: it predicts where `writeDocument` will land, and that follows the symlinks on
|
|
105
|
+
* disk rather than the ones a commit records.
|
|
106
|
+
*
|
|
107
|
+
* @param scope - How much of the document this operation owns; defaults to the strict
|
|
108
|
+
* `declared-fields-only`, so a new verb is guarded unless it opts out deliberately.
|
|
109
|
+
*/
|
|
110
|
+
export declare function commitMemoryTransition(root: string, prepared: PreparedMemoryTransition, content: string, message: string, expected?: Readonly<Record<string, string | undefined>>, scope?: DocumentScope): CoreResult<string>;
|
|
111
|
+
//# sourceMappingURL=memory-transition.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-transition.d.ts","sourceRoot":"","sources":["../../src/core/memory-transition.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAgB,YAAY,EAAE,MAAM,WAAW,CAAC;AAMvF,OAAO,EAAmB,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AAG3D,kGAAkG;AAClG,MAAM,WAAW,wBAAwB;IACvC;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,oFAAoF;IACpF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,gDAAgD;IAChD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,wEAAwE;IACxE,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACxD,mEAAmE;IACnE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;CACrB;AAyBD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,YAAY,GAAG,UAAU,CAAC,wBAAwB,CAAC,CA2DxH;AAyCD;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,EACtD,KAAK,EAAE,aAAa,GACnB,MAAM,EAAE,CAQV;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,wBAAwB,EAClC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAM,EAC3D,KAAK,GAAE,aAAsC,GAC5C,UAAU,CAAC,MAAM,CAAC,CA2BpB"}
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.prepareMemoryTransition = prepareMemoryTransition;
|
|
4
|
+
exports.verifyCommittedScope = verifyCommittedScope;
|
|
5
|
+
exports.commitMemoryTransition = commitMemoryTransition;
|
|
6
|
+
/**
|
|
7
|
+
* The shared skeleton of every Memory state-transition verb (task-045-memory-submit; reused by
|
|
8
|
+
* `memory approve`/`reject`/`deprecate`, task-046/047/048).
|
|
9
|
+
*
|
|
10
|
+
* A transition verb always does the same two things around its own verb-specific rules:
|
|
11
|
+
*
|
|
12
|
+
* 1. {@link prepareMemoryTransition} — find the document by its bare id (spec-008-cli-grammar §7), read
|
|
13
|
+
* its declared `type` and current `status`, and resolve the legal target for the verb **against the
|
|
14
|
+
* `memory.yaml` committed at `HEAD`** (task-091, `bug-081`, `dl-080` (B)). Every expected
|
|
15
|
+
* refusal is returned as a `CoreResult.error` (exit `1`), **before anything is written**, so "the
|
|
16
|
+
* state is unchanged" (P1.6 sc.2, REQ-STATE-01) holds by construction.
|
|
17
|
+
* 2. {@link commitMemoryTransition} — write the new bytes and turn them into exactly one commit scoped
|
|
18
|
+
* to that one document.
|
|
19
|
+
*
|
|
20
|
+
* What happens in between (required-field checks, which fields change, the commit message) is the
|
|
21
|
+
* verb's own business; see `memorySubmitFn` in `./index.ts`.
|
|
22
|
+
*/
|
|
23
|
+
const path_1 = require("path");
|
|
24
|
+
const memory_1 = require("../memory");
|
|
25
|
+
const storage_1 = require("../storage");
|
|
26
|
+
const validation_1 = require("../validation");
|
|
27
|
+
const confinement_1 = require("./confinement");
|
|
28
|
+
const loaders_1 = require("./loaders");
|
|
29
|
+
const types_1 = require("./types");
|
|
30
|
+
const write_guard_1 = require("./write-guard");
|
|
31
|
+
/**
|
|
32
|
+
* A second sentence for a refusal, appended **only** when the working tree's `memory.yaml` and the
|
|
33
|
+
* committed one actually differ — the diagnostic half of `task-090`'s `workingTreeWouldGrant`, on
|
|
34
|
+
* this surface.
|
|
35
|
+
*
|
|
36
|
+
* Under a committed baseline a refusal can contradict the file open in the user's editor, and
|
|
37
|
+
* `memory.yaml` is precisely the file an author edits while designing a new type or machine
|
|
38
|
+
* (`bug-081`'s Notes: "a half-finished edit is an ordinary state to be in"). So when the two copies
|
|
39
|
+
* disagree, say which one decided. It is computed *after* the decision and is empty on any failure
|
|
40
|
+
* to ask git, so no branch of it can change an outcome.
|
|
41
|
+
*/
|
|
42
|
+
function uncommittedMachineNote(root) {
|
|
43
|
+
try {
|
|
44
|
+
if ((0, storage_1.pathPorcelainStatus)(root, loaders_1.MEMORY_YAML_PATH) === '')
|
|
45
|
+
return '';
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return '';
|
|
49
|
+
}
|
|
50
|
+
return (` — note that '${loaders_1.MEMORY_YAML_PATH}' carries uncommitted modifications and the state machine is read from the ` +
|
|
51
|
+
`committed copy (dl-080); commit '${loaders_1.MEMORY_YAML_PATH}' first if this transition depends on that change`);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Locate document `id` and resolve verb `op` on it, **against the `memory.yaml` committed at
|
|
55
|
+
* `HEAD`** (`loadMemoryYamlAtHead`).
|
|
56
|
+
*
|
|
57
|
+
* The baseline is the point (task-091, `bug-081`, `dl-080` option (B)). This function takes no
|
|
58
|
+
* `MemoryYaml`: a caller cannot hand it a working-tree machine even by accident, which is what makes
|
|
59
|
+
* the committed baseline a property of the read itself rather than of a precondition someone must
|
|
60
|
+
* remember to run. An uncommitted edit to a type's `sequence` used to decide both the transition a
|
|
61
|
+
* verb performed and the `status` it committed — through `memory submit`, which requires no
|
|
62
|
+
* authority — and stranded the element in a state the committed machine rejects.
|
|
63
|
+
*
|
|
64
|
+
* Refusals, all returned (never thrown) and all exit `1`:
|
|
65
|
+
*
|
|
66
|
+
* - `HEAD` holds no `memory.yaml` → `VALIDATION`, fail-closed: with no committed machine there is no
|
|
67
|
+
* `from → to` to record, and the only fail-open available is the working tree, which is the defect.
|
|
68
|
+
* `wingfoil init` commits the scaffold, so no legitimate flow reaches this;
|
|
69
|
+
* - the committed `memory.yaml` does not parse or validate → `VALIDATION`, same reasoning;
|
|
70
|
+
* - no document carries that frontmatter `id` → `NOT_FOUND` `document not found: <id>` (P1.6 sc.3);
|
|
71
|
+
* - its `type` is not registered → `NOT_FOUND` with `memory add`'s unknown-type message;
|
|
72
|
+
* - its `status` is not a state of the type → `VALIDATION` `invalid state '<s>' for type '<t>'`
|
|
73
|
+
* (task-036's `validateFrontmatterState`);
|
|
74
|
+
* - the verb is illegal from that state → `INVALID_TRANSITION` with the `dl-032` contract message
|
|
75
|
+
* (`resolveTypeTransition`), the engine's explanation in `details.issues[0].detail`.
|
|
76
|
+
*
|
|
77
|
+
* The last four keep their pinned messages verbatim as the first sentence;
|
|
78
|
+
* {@link uncommittedMachineNote} may append a second one.
|
|
79
|
+
*/
|
|
80
|
+
function prepareMemoryTransition(root, id, op) {
|
|
81
|
+
let memoryYaml;
|
|
82
|
+
try {
|
|
83
|
+
memoryYaml = (0, loaders_1.loadMemoryYamlAtHead)(root);
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
if (!(error instanceof validation_1.ValidationError))
|
|
87
|
+
throw error;
|
|
88
|
+
return (0, types_1.coreErr)({
|
|
89
|
+
code: 'VALIDATION',
|
|
90
|
+
message: `cannot resolve the state machine: the committed '${loaders_1.MEMORY_YAML_PATH}' (at HEAD) is not readable as a Memory ` +
|
|
91
|
+
`configuration: ${error.message}. A transition is decided by the machine the repository records (dl-080), so ` +
|
|
92
|
+
'the committed file must be valid; fix it and commit the fix, then retry.',
|
|
93
|
+
details: { issues: error.issues },
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
if (memoryYaml === null) {
|
|
97
|
+
return (0, types_1.coreErr)({
|
|
98
|
+
code: 'VALIDATION',
|
|
99
|
+
message: `cannot resolve the state machine: '${loaders_1.MEMORY_YAML_PATH}' is not committed at HEAD. A transition is decided by ` +
|
|
100
|
+
`the machine the repository records, not by a working tree (dl-080); commit '${loaders_1.MEMORY_YAML_PATH}' first, then retry.`,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
const found = (0, memory_1.findMemoryDocumentById)(root, memoryYaml, id);
|
|
104
|
+
if (!found) {
|
|
105
|
+
return (0, types_1.coreErr)({ code: 'NOT_FOUND', message: `document not found: ${id}${uncommittedMachineNote(root)}` });
|
|
106
|
+
}
|
|
107
|
+
const type = found.frontmatter.type;
|
|
108
|
+
if (typeof type !== 'string' || memoryYaml.types[type] === undefined) {
|
|
109
|
+
return (0, types_1.coreErr)({
|
|
110
|
+
code: 'NOT_FOUND',
|
|
111
|
+
message: `unknown memory type '${String(type)}' (not defined in memory.yaml)${uncommittedMachineNote(root)}`,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
// Always resolves: the type is registered (checked immediately above), and since task-071
|
|
115
|
+
// (`bug-030-init-memory-yaml-has-no-state-machine`) a registered type with no `states` and no
|
|
116
|
+
// `defaults.states` falls back to the engine's built-in `DEFAULT_STATE_MACHINE` (REQ-STATE-08)
|
|
117
|
+
// instead of throwing. The `VALIDATION` refusal that used to guard this call is therefore gone with
|
|
118
|
+
// the condition it reported — the only throw left in `resolveStateMachine` is for an UNregistered
|
|
119
|
+
// type, which the `NOT_FOUND` above has already returned for.
|
|
120
|
+
const machine = (0, memory_1.resolveStateMachine)(memoryYaml, type);
|
|
121
|
+
const status = found.frontmatter.status;
|
|
122
|
+
// A missing or non-string `status` is reported as an invalid state, never as the text 'undefined'.
|
|
123
|
+
const from = typeof status === 'string' ? status : String(status ?? '');
|
|
124
|
+
try {
|
|
125
|
+
(0, memory_1.validateFrontmatterState)(machine, type, from, found.path);
|
|
126
|
+
const to = (0, memory_1.resolveTypeTransition)(memoryYaml, type, from, op, found.path);
|
|
127
|
+
const content = (0, storage_1.readDocument)((0, path_1.join)(root, found.path));
|
|
128
|
+
return (0, types_1.coreOk)({ memoryYaml, id, type, path: found.path, frontmatter: found.frontmatter, content, from, to });
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
if (!(error instanceof validation_1.ValidationError))
|
|
132
|
+
throw error;
|
|
133
|
+
const code = error.issues.some((issue) => issue.code === memory_1.E_INVALID_TRANSITION) ? 'INVALID_TRANSITION' : 'VALIDATION';
|
|
134
|
+
const message = error.issues.map((issue) => issue.message).join('; ') + uncommittedMachineNote(root);
|
|
135
|
+
return (0, types_1.coreErr)({ code, message, details: { issues: error.issues } });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Refuse, before anything is written, a document that already carries modifications this transition
|
|
140
|
+
* does not own (task-088, `bug-076`; AC2 option (b)).
|
|
141
|
+
*
|
|
142
|
+
* `commitPaths` bounds a commit by **pathspec**, not by content: `git add -- <path>` stages the whole
|
|
143
|
+
* file, so an uncommitted body paragraph or an unowned frontmatter field rode into an approval under a
|
|
144
|
+
* subject declaring only a state change — and the old post-condition could not see it, because it
|
|
145
|
+
* compared the rendering against the same dirty file. Every recorded fact in such a commit is true
|
|
146
|
+
* (`P1.7`'s approver, timestamp and reason are all present and all correct); the commit is simply
|
|
147
|
+
* larger than what it claims.
|
|
148
|
+
*
|
|
149
|
+
* **Why refusing rather than committing only the status hunk.** Partial staging is undefined here
|
|
150
|
+
* before it is difficult: there are two candidate baselines on disk (the index and the working tree)
|
|
151
|
+
* and they can disagree, so "only the status hunk" does not say onto which text. Worse, the verb reads
|
|
152
|
+
* `status` itself from the working tree, so a hand-edited-but-uncommitted `status` would have the
|
|
153
|
+
* commit record `draft → approved` under a subject declaring `pending → approved` — the audit trail
|
|
154
|
+
* asserting something that did not happen, which is the defect, not a fix for it. And the edits a
|
|
155
|
+
* partial commit left behind would be invisible, deferred, and swept into whatever commits next. A
|
|
156
|
+
* refusal costs the user two commands, now, on a document this function has provably not touched.
|
|
157
|
+
*
|
|
158
|
+
* Runs only under `declared-fields-only`; `memory submit` is entitled to carry content and is
|
|
159
|
+
* therefore not guarded (see {@link DocumentScope}). The verb's own writes — `reject`'s
|
|
160
|
+
* `rejection_reason`, every verb's `status` — happen *after* this check, on text it has just certified
|
|
161
|
+
* equal to `HEAD`, so no verb is caught by its own guard.
|
|
162
|
+
*
|
|
163
|
+
* Since `task-092` the gate itself lives in {@link requireUnmodifiedTarget} (`./write-guard.ts`),
|
|
164
|
+
* shared with the six non-transition write verbs `dl-080` reaches through `bug-078`; only the wording
|
|
165
|
+
* below is this call site's own, so the two families cannot drift apart on what "modified" means.
|
|
166
|
+
*/
|
|
167
|
+
const TRANSITION_CONTRACT = {
|
|
168
|
+
noun: 'document',
|
|
169
|
+
owner: 'transition',
|
|
170
|
+
records: 'A state-transition commit records the status change and nothing else',
|
|
171
|
+
};
|
|
172
|
+
function requireUnmodifiedDocument(root, prepared) {
|
|
173
|
+
return (0, write_guard_1.requireUnmodifiedTarget)(root, prepared.path, TRANSITION_CONTRACT, prepared.content);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Verify what the new commit **contains**, by diffing it against its own parent (task-088, AC3).
|
|
177
|
+
*
|
|
178
|
+
* This is the half that makes the guard above checkable rather than hopeful. Reading the file on disk
|
|
179
|
+
* answers "does the document now say `approved`?" — true no matter what else rode along; `HEAD~1..HEAD`
|
|
180
|
+
* is not. Same shape as `task-080`'s lockfile guard: assert the diff, not the end state. The parent is
|
|
181
|
+
* `<sha>^`, or git's empty tree for a root commit, so the check is total rather than conditional.
|
|
182
|
+
*
|
|
183
|
+
* It is an **alarm, not a rollback**: it can only run once the commit exists, and rewriting history
|
|
184
|
+
* behind the user's back is a worse failure than reporting one (`dl-035` points the same way). With
|
|
185
|
+
* {@link requireUnmodifiedDocument} in place it should be unreachable, which is the point.
|
|
186
|
+
*/
|
|
187
|
+
function verifyCommittedScope(root, sha, path, expected, scope) {
|
|
188
|
+
// The "did anything else ride in" half is shared with `verifyCommittedPaths` (task-092), so the
|
|
189
|
+
// Memory and configuration post-conditions cannot disagree about what a commit's scope is.
|
|
190
|
+
const { parent, problems } = (0, write_guard_1.undeclaredCommittedPaths)(root, sha, [path]);
|
|
191
|
+
const after = (0, storage_1.readPathAtRev)(root, sha, path);
|
|
192
|
+
if (after === null)
|
|
193
|
+
return [...problems, `it does not contain '${path}'`];
|
|
194
|
+
return [...problems, ...(0, memory_1.verifyDocumentEdit)((0, storage_1.readPathAtRev)(root, parent, path) ?? '', after, expected, scope)];
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Write `content` over the prepared document and commit exactly that one path with `message`,
|
|
198
|
+
* returning the new commit's sha (`commitPaths`, task-018; scoped to that path even when other changes
|
|
199
|
+
* are staged, bug-027). Callers must have run the git-identity pre-flight (REQ-SEC-01) and every refusal
|
|
200
|
+
* check first; this step is the only write.
|
|
201
|
+
*
|
|
202
|
+
* Four checks bound what reaches the repository, in this order — they are not redundant, they catch
|
|
203
|
+
* different defects:
|
|
204
|
+
*
|
|
205
|
+
* 1. **The document is the project's to write** ({@link requireConfinedWriteTarget}, REQ-SEC-06) —
|
|
206
|
+
* catches a wrong *place*, in the two shapes a write can meet it. A type directory that is a
|
|
207
|
+
* symlink out of the project puts the document outside the root (`bug-117`), where `writeDocument`
|
|
208
|
+
* rewrites a file the repository does not own and `git add` then fails with its own text; and a
|
|
209
|
+
* **document** that is itself a symlink does the same with no symlinked directory anywhere,
|
|
210
|
+
* because `writeFileSync` follows the link that `unlinkSync` would merely remove (`bug-120` D2,
|
|
211
|
+
* measured on all four verbs). `bug-117` reports the first for `memory add`, whose target is
|
|
212
|
+
* resolved by `resolveConfinedMemoryPath`; these four verbs never ask that resolver anything —
|
|
213
|
+
* they locate an existing document — so both questions are asked here, about the path they are
|
|
214
|
+
* about to write. It runs **first**, and for every `scope`: the question "is this file ours at
|
|
215
|
+
* all" precedes every question about its content, and check 2 cannot stand in for it (`git status
|
|
216
|
+
* --porcelain` reports a path beyond a symbolic link as clean, which is `bug-118`).
|
|
217
|
+
* 2. **The working tree is unmodified** ({@link requireUnmodifiedDocument}, `declared-fields-only`
|
|
218
|
+
* only) — catches a wrong *baseline*: content that was already on disk before the verb ran
|
|
219
|
+
* (`bug-076`).
|
|
220
|
+
* 3. **The rendering is in scope** ({@link verifyDocumentEdit} against the prepared document) —
|
|
221
|
+
* catches a defect in the *editor*: `status` must be the prepared target, every field in `expected`
|
|
222
|
+
* must have its value (`undefined` = absent), and no other field and no byte of the body may have
|
|
223
|
+
* moved (the `bug-041` class). Runs under `declared-fields-only` for **every** verb, `submit`
|
|
224
|
+
* included: `renderSubmitDocument` owns `status` and `rejection_reason` alone, and the content
|
|
225
|
+
* `submit` carries is already in the prepared document it is compared against.
|
|
226
|
+
* 4. **The commit contains only the declared change** ({@link verifyCommittedScope} against
|
|
227
|
+
* `HEAD~1`) — the post-condition proper, and the only one that measures the artefact of record.
|
|
228
|
+
*
|
|
229
|
+
* Checks 1–3 report a `VALIDATION` error (exit 1) with nothing written or committed. Check 4 can
|
|
230
|
+
* only report; see {@link verifyCommittedScope}.
|
|
231
|
+
*
|
|
232
|
+
* Check 1 resolves on the **working tree**, not at `HEAD`, which departs from `command-baseline`'s
|
|
233
|
+
* rule for a gating read. The argument is `task-102`'s, recorded in
|
|
234
|
+
* `dl-086-a-guard-over-a-filesystem-effect-resolves-on-the-filesystem` (`in-discussion`) and cited
|
|
235
|
+
* rather than re-made: it predicts where `writeDocument` will land, and that follows the symlinks on
|
|
236
|
+
* disk rather than the ones a commit records.
|
|
237
|
+
*
|
|
238
|
+
* @param scope - How much of the document this operation owns; defaults to the strict
|
|
239
|
+
* `declared-fields-only`, so a new verb is guarded unless it opts out deliberately.
|
|
240
|
+
*/
|
|
241
|
+
function commitMemoryTransition(root, prepared, content, message, expected = {}, scope = 'declared-fields-only') {
|
|
242
|
+
const owned = { status: prepared.to, ...expected };
|
|
243
|
+
const confined = (0, confinement_1.requireConfinedWriteTarget)(root, prepared.path, 'write');
|
|
244
|
+
if (!confined.ok)
|
|
245
|
+
return confined;
|
|
246
|
+
if (scope === 'declared-fields-only') {
|
|
247
|
+
const unmodified = requireUnmodifiedDocument(root, prepared);
|
|
248
|
+
if (!unmodified.ok)
|
|
249
|
+
return unmodified;
|
|
250
|
+
}
|
|
251
|
+
const problems = (0, memory_1.verifyDocumentEdit)(prepared.content, content, owned, 'declared-fields-only');
|
|
252
|
+
if (problems.length > 0) {
|
|
253
|
+
return (0, types_1.coreErr)({
|
|
254
|
+
code: 'VALIDATION',
|
|
255
|
+
message: `refusing to write ${prepared.path}: the rendered document failed its post-condition: ${problems.join('; ')}`,
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
(0, storage_1.writeDocument)((0, path_1.join)(root, prepared.path), content);
|
|
259
|
+
const sha = (0, storage_1.commitPaths)(root, [prepared.path], message);
|
|
260
|
+
const leaked = verifyCommittedScope(root, sha, prepared.path, owned, scope);
|
|
261
|
+
if (leaked.length > 0) {
|
|
262
|
+
return (0, types_1.coreErr)({
|
|
263
|
+
code: 'VALIDATION',
|
|
264
|
+
message: `commit ${sha} carries more than the change it declares: ${leaked.join('; ')}`,
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
return (0, types_1.coreOk)(sha);
|
|
268
|
+
}
|
|
269
|
+
//# sourceMappingURL=memory-transition.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-transition.js","sourceRoot":"","sources":["../../src/core/memory-transition.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `CoreModule` registry shape (spec-006-core-domain-api §2, §4 — task-006). `src/cli` and
|
|
3
|
+
* `src/mcp` both import the same `CoreModule[]` array (`./index.ts`'s `CORE_MODULES`) and register
|
|
4
|
+
* every operation they find — neither surface is permitted a hardcoded allow/deny list of operation
|
|
5
|
+
* names (§4.2). This module owns: the registry types themselves, the one mechanical
|
|
6
|
+
* name-derivation function both adapters use to turn an operation's camelCase name into its CLI
|
|
7
|
+
* verb / MCP Tool-and-Resource verb segment (so neither adapter hand-maintains a second name
|
|
8
|
+
* mapping — spec-006 §5), a deterministic (REQ-SYS-07) enumerator, and the generic parity-diff
|
|
9
|
+
* primitive the REQ-SYS-05 fit criterion's "0 unmatched operations" assertion is built on.
|
|
10
|
+
*/
|
|
11
|
+
import type { CoreResult } from './types';
|
|
12
|
+
/**
|
|
13
|
+
* The signature every core operation implements (spec-006 §2): an async function from typed params
|
|
14
|
+
* `P` to a `CoreResult<R>` — never a thrown error for an expected domain failure (see {@link CoreResult}).
|
|
15
|
+
*/
|
|
16
|
+
export type CoreFn<P, R> = (params: P) => Promise<CoreResult<R>>;
|
|
17
|
+
/**
|
|
18
|
+
* One value-bearing CLI option an operation accepts (task-020-implement-memory-add) — e.g.
|
|
19
|
+
* `--type <value>`, `--title <value>`, `--tags <value>` for `memory add`. Distinct from
|
|
20
|
+
* {@link CoreOperation.flags}, which are BOOLEAN presence flags (`--list`): an option carries a
|
|
21
|
+
* string VALUE (`.option('--<name> <value>')`). `required` is declarative metadata: the operation's
|
|
22
|
+
* own `CoreFn` enforces it (throwing a `UsageError` → exit 2, consistent with `dna set`'s in-fn arg
|
|
23
|
+
* validation, task-025); it is surfaced here so `src/cli/program.ts` (and, later, the MCP Tool
|
|
24
|
+
* input schema) can describe the option without duplicating that knowledge. task-021's
|
|
25
|
+
* `memory submit`/etc. reuse this exact mechanism — the ONE value-option seam, not a parallel one.
|
|
26
|
+
*/
|
|
27
|
+
export interface CoreOption {
|
|
28
|
+
readonly name: string;
|
|
29
|
+
readonly required?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* What `--help` says about this option. Optional and additive: an option that declares none keeps
|
|
32
|
+
* the generic `"{name} value"` text `src/cli/program.ts` has always rendered. It exists because a
|
|
33
|
+
* convention a grammar cannot show must be *stated* somewhere a user reads — `dna add|remove|update
|
|
34
|
+
* --value` means the new entry's identity at a collection and the new value at a leaf
|
|
35
|
+
* (`dl-081-dna-mutation-surface-shape`, task-093), which is exactly the kind of thing that otherwise
|
|
36
|
+
* gets discovered rather than documented.
|
|
37
|
+
*/
|
|
38
|
+
readonly description?: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* One domain operation both surfaces derive from (spec-006 §2): its camelCase `name`, whether it
|
|
42
|
+
* `mutates` (Tool vs Resource / write vs read), its `fn`, and its optional declarative CLI `flags`
|
|
43
|
+
* and value-bearing `options`. The single source of truth `src/cli` and `src/mcp` register from.
|
|
44
|
+
*/
|
|
45
|
+
export interface CoreOperation<P = unknown, R = unknown> {
|
|
46
|
+
/** camelCase, `{module}{Verb}` (spec-006 §5) — e.g. `memoryApprove`, `dnaShow`. */
|
|
47
|
+
readonly name: string;
|
|
48
|
+
/** `true` => MCP Tool only; `false` => MCP Resource only. CLI exposes both regardless (§2). */
|
|
49
|
+
readonly mutates: boolean;
|
|
50
|
+
readonly fn: CoreFn<P, R>;
|
|
51
|
+
/**
|
|
52
|
+
* Boolean CLI flag names this operation accepts beyond the global flags (spec-008-cli-grammar §2)
|
|
53
|
+
* — e.g. `['list']` for `paths`'s `--list` drill-down flag (task-028-implement-paths-category,
|
|
54
|
+
* P2.5). Additive and optional: an operation that declares none keeps exactly the bare-`{ root }`
|
|
55
|
+
* (+ generic `positional`) shape task-026 established. The bare positional argument itself is NOT
|
|
56
|
+
* declared here — it stays the single generic `[positional]` `src/cli/program.ts` registers on
|
|
57
|
+
* every command (task-026's seam, {@link ParamsContext.positional}); only extra `--{name}` flags
|
|
58
|
+
* are per-operation, since Commander rejects an unknown option so each must be registered
|
|
59
|
+
* explicitly. `src/cli/program.ts` registers one `--{name}` option per entry and threads the
|
|
60
|
+
* parsed boolean into {@link ParamsContext.flags}.
|
|
61
|
+
*/
|
|
62
|
+
readonly flags?: readonly string[];
|
|
63
|
+
/**
|
|
64
|
+
* Value-bearing CLI options this operation accepts beyond the global options
|
|
65
|
+
* (task-020-implement-memory-add — e.g. `[{name:'type',required:true}, {name:'title',required:true},
|
|
66
|
+
* {name:'tags'}]` for `memory add`). Additive alongside {@link flags}: an operation declaring none
|
|
67
|
+
* keeps exactly the bare shape it had before. `src/cli/program.ts` registers one
|
|
68
|
+
* `--{name} <value>` Commander option per entry and threads the parsed values into
|
|
69
|
+
* {@link ParamsContext.options}; the MCP surface never populates them (task-030 wires the Tool input
|
|
70
|
+
* schema). See {@link CoreOption}.
|
|
71
|
+
*/
|
|
72
|
+
readonly options?: readonly CoreOption[];
|
|
73
|
+
}
|
|
74
|
+
/** One pillar's operation group (spec-006 §2) — a `name` (the `wingfoil <noun>` segment) and its operations. */
|
|
75
|
+
export interface CoreModule {
|
|
76
|
+
/** e.g. `"memory"`, `"dna"`, `"workflow"`, `"directives"` — the `wingfoil <noun>` segment. */
|
|
77
|
+
readonly name: string;
|
|
78
|
+
/** This module's operations, keyed by camelCase operation name (see {@link CoreOperation}). */
|
|
79
|
+
readonly operations: Readonly<Record<string, CoreOperation>>;
|
|
80
|
+
}
|
|
81
|
+
/** One operation, paired with the module that declares it — the flat shape both adapters iterate. */
|
|
82
|
+
export interface EnumeratedOperation {
|
|
83
|
+
readonly module: CoreModule;
|
|
84
|
+
readonly operation: CoreOperation;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Flatten `modules` into every declared operation, sorted deterministically by `(module.name,
|
|
88
|
+
* operation.name)` (REQ-SYS-07 — no unordered `Object.values`/iteration in a context-building
|
|
89
|
+
* path). Both `src/cli` and `src/mcp` register from this single enumerator, never from their own
|
|
90
|
+
* ad hoc traversal of `modules`/`Object.keys`.
|
|
91
|
+
*/
|
|
92
|
+
export declare function enumerateOperations(modules: readonly CoreModule[]): EnumeratedOperation[];
|
|
93
|
+
/**
|
|
94
|
+
* Derive an operation's CLI verb / MCP verb segment from its camelCase `{module}{Verb}` name
|
|
95
|
+
* (spec-006 §5), mechanically — this plus `mutates` is "the one place surface-routing logic
|
|
96
|
+
* lives" (§2): given `moduleName = "memory"` and `operationName = "memoryApprove"`, returns
|
|
97
|
+
* `"approve"`; a multi-word verb (`"workflowSetTeam"`) becomes kebab-case (`"set-team"`) so it
|
|
98
|
+
* renders as a single CLI token / Tool-name segment. Falls back to the full operation name,
|
|
99
|
+
* kebab-cased, when it does not start with the module's name — a config mistake the caller should
|
|
100
|
+
* catch earlier, but this function degrades rather than throwing, so a single bad registration
|
|
101
|
+
* cannot crash an adapter's entire registration pass.
|
|
102
|
+
*/
|
|
103
|
+
export declare function deriveVerb(moduleName: string, operationName: string): string;
|
|
104
|
+
/**
|
|
105
|
+
* What a surface adapter (`src/cli`, `src/mcp`) knows about the call it is about to make — enough
|
|
106
|
+
* for a caller-supplied {@link ParamsBuilder} to construct that operation's actual typed params.
|
|
107
|
+
* Neither adapter hardcodes per-operation argv/Tool-input parsing here (that is each future
|
|
108
|
+
* operation's own CLI-command / Tool-input-schema spec, per spec-005's own scope note) — every
|
|
109
|
+
* operation registered through task-006/task-027 took a bare `{ root }`; task-028-implement-paths-category
|
|
110
|
+
* is the first to need more, and does so exactly the way this interface's doc comment always
|
|
111
|
+
* anticipated: a richer `buildParams`, not a registrar change. `positional` is registered
|
|
112
|
+
* generically as a single `[positional]` on every derived CLI command (`src/cli/program.ts`) and
|
|
113
|
+
* threaded through `command.run` into `ctx.positional`; `flags` is populated from the matching
|
|
114
|
+
* operation's `CoreOperation.flags` declaration (`./registry.ts`). The MCP adapter's own mechanical,
|
|
115
|
+
* zero-argument Resource/Tool registration (`src/mcp/registrar.ts`) never populates either — a
|
|
116
|
+
* `buildParams` that ignores both (every `ParamsBuilder` before this task) keeps working unchanged.
|
|
117
|
+
*/
|
|
118
|
+
export interface ParamsContext {
|
|
119
|
+
readonly moduleName: string;
|
|
120
|
+
readonly operationName: string;
|
|
121
|
+
readonly root: string;
|
|
122
|
+
/**
|
|
123
|
+
* The bare CLI positional argument following `<noun> <verb>` (or `<noun>` for a flat command),
|
|
124
|
+
* if the invocation supplied one (e.g. `wingfoil dna show tech_stack` -> `"tech_stack"`,
|
|
125
|
+
* `wingfoil paths sources` -> `"sources"`) — task-026-implement-dna-show's generic seam, kept as
|
|
126
|
+
* the single source of truth. Deliberately singular and untyped beyond `string | undefined`: any
|
|
127
|
+
* operation's `buildParams` reads it under whatever param name that operation's own `CoreFn`
|
|
128
|
+
* expects (`dnaShow`'s `section`, `paths`'s `category`), and an operation needing a *required*
|
|
129
|
+
* positional still owns that validation in its own `CoreFn` — this seam only carries the raw
|
|
130
|
+
* value from the CLI adapter through to `buildParams`. The MCP surface has no equivalent
|
|
131
|
+
* mechanical concept today (a zero-argument Resource template can't carry one — spec-004 §2.1's
|
|
132
|
+
* own `wingfoil://dna/{section}` addressing is the MCP-side answer, wired independently in
|
|
133
|
+
* `src/mcp/dna-resource.ts`), so an MCP `buildParams` simply never sets this field.
|
|
134
|
+
*/
|
|
135
|
+
readonly positional?: string;
|
|
136
|
+
/**
|
|
137
|
+
* The FULL list of bare CLI positional arguments following `<noun> <verb>` (or `<noun>` for a flat
|
|
138
|
+
* command) — task-025-implement-dna-set's additive extension of the single-`positional` seam, needed
|
|
139
|
+
* by the first operation taking two data inputs (`dna set <key> <value>` -> `['<key>', '<value>']`).
|
|
140
|
+
* Since `dl-082-cli-parameter-shape` (task-093) every command reads at most ONE positional — the
|
|
141
|
+
* identity of its target — and `dna set`'s value travels in `--value`, so the list is now a
|
|
142
|
+
* uniform seam rather than one shaped by a single two-input verb; it stays variadic so an
|
|
143
|
+
* operation can refuse an extra positional with its own message.
|
|
144
|
+
* {@link positional} is exactly `positionals?.[0]` and is kept unchanged for the single-positional
|
|
145
|
+
* read ops that predate this (`dna show [section]`, `paths [category]`), so their `buildParams` and
|
|
146
|
+
* `CoreFn`s are untouched; an op that must validate its positional (`dnaSet` and the three mutation
|
|
147
|
+
* verbs, which reject an extra one) reads `positionals`
|
|
148
|
+
* instead. Same additive contract as {@link positional}/{@link flags}: the MCP surface never
|
|
149
|
+
* populates it (a zero-argument Tool/Resource template carries no positional — see {@link positional}),
|
|
150
|
+
* so an MCP `buildParams` simply never sets it.
|
|
151
|
+
*/
|
|
152
|
+
readonly positionals?: readonly string[];
|
|
153
|
+
/**
|
|
154
|
+
* This operation's declared {@link CoreOperation.flags} names mapped to their parsed boolean
|
|
155
|
+
* values (task-028-implement-paths-category — e.g. `{ list: true }` for `wingfoil paths sources
|
|
156
|
+
* --list`). Additive alongside `positional`: an operation declaring no flags never has this set,
|
|
157
|
+
* and the MCP surface never populates it (same rationale as `positional`).
|
|
158
|
+
*/
|
|
159
|
+
readonly flags?: Readonly<Record<string, boolean>>;
|
|
160
|
+
/**
|
|
161
|
+
* This operation's declared {@link CoreOperation.options} names mapped to their parsed string
|
|
162
|
+
* VALUES (task-020-implement-memory-add — e.g. `{ type: 'decision', title: 'Use PostgreSQL' }` for
|
|
163
|
+
* `wingfoil memory add --type decision --title 'Use PostgreSQL'`). Additive alongside `flags`: an
|
|
164
|
+
* operation declaring no value options never has this set, and the MCP surface never populates it
|
|
165
|
+
* (a zero-argument Tool template carries none — same rationale as `positional`/`flags`; task-030
|
|
166
|
+
* wires the MCP Tool input schema separately). An absent optional option is simply omitted from the
|
|
167
|
+
* record rather than present-as-`undefined`.
|
|
168
|
+
*/
|
|
169
|
+
readonly options?: Readonly<Record<string, string>>;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* A surface-supplied function that turns a {@link ParamsContext} into the typed params one operation's
|
|
173
|
+
* {@link CoreFn} expects — the single per-surface seam where CLI/MCP-specific argument shaping lives,
|
|
174
|
+
* so the registrars stay operation-agnostic (spec-006 §2).
|
|
175
|
+
*/
|
|
176
|
+
export type ParamsBuilder = (ctx: ParamsContext) => unknown;
|
|
177
|
+
/** The result of {@link computeParityDiff}: entries present on only one side, each sorted. */
|
|
178
|
+
export interface ParityDiff {
|
|
179
|
+
readonly onlyInA: readonly string[];
|
|
180
|
+
readonly onlyInB: readonly string[];
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Set-difference both ways between two enumerations of the same kind of key (e.g. `"noun verb"`
|
|
184
|
+
* CLI commands vs. the same keys' MCP Tool names, normalized to a common form by the caller) —
|
|
185
|
+
* the REQ-SYS-05 fit-criterion primitive: "reports 0 [operations] without a matching [counterpart]"
|
|
186
|
+
* in each direction. Both output lists are sorted, independent of input order (REQ-SYS-07).
|
|
187
|
+
*/
|
|
188
|
+
export declare function computeParityDiff(a: readonly string[], b: readonly string[]): ParityDiff;
|
|
189
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/core/registry.ts"],"names":[],"mappings":"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"}
|