@openparachute/vault 0.6.5-rc.11 → 0.6.5-rc.12

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.
@@ -4046,7 +4046,7 @@ describe("query-notes link expansion", async () => {
4046
4046
  });
4047
4047
 
4048
4048
  // ---------------------------------------------------------------------------
4049
- // Tag hierarchy via tags.parent_names (post-v14, patterns/tag-data-model.md)
4049
+ // Tag hierarchy via tags.parent_names (post-v14, docs/contracts/tag-data-model.md)
4050
4050
  // ---------------------------------------------------------------------------
4051
4051
 
4052
4052
  describe("tag hierarchy (tags.parent_names)", async () => {
@@ -5107,7 +5107,7 @@ describe("schema inheritance via parent_names (vault#270)", async () => {
5107
5107
  });
5108
5108
  });
5109
5109
 
5110
- describe("expandTagsWithDescendants (tag-scoped tokens — patterns/tag-scoped-tokens.md)", async () => {
5110
+ describe("expandTagsWithDescendants (tag-scoped tokens — docs/contracts/tag-scoped-tokens.md)", async () => {
5111
5111
  it("returns the union of root + every descendant per tags.parent_names", async () => {
5112
5112
  await store.upsertTagRecord("health/food", { parent_names: ["health"] });
5113
5113
  await store.upsertTagRecord("health/food/breakfast", { parent_names: ["health/food"] });
@@ -5143,10 +5143,10 @@ describe("expandTagsWithDescendants (tag-scoped tokens — patterns/tag-scoped-t
5143
5143
  });
5144
5144
 
5145
5145
  // ---------------------------------------------------------------------------
5146
- // Tag record API — patterns/tag-data-model.md
5146
+ // Tag record API — docs/contracts/tag-data-model.md
5147
5147
  // ---------------------------------------------------------------------------
5148
5148
 
5149
- describe("tag record API (patterns/tag-data-model.md)", async () => {
5149
+ describe("tag record API (docs/contracts/tag-data-model.md)", async () => {
5150
5150
  it("upsertTagRecord persists description + fields + relationships + parent_names", async () => {
5151
5151
  await store.upsertTagRecord("project", {
5152
5152
  description: "long-running deliverable",
@@ -5430,7 +5430,7 @@ describe("tag record API (patterns/tag-data-model.md)", async () => {
5430
5430
  });
5431
5431
 
5432
5432
  // ---------------------------------------------------------------------------
5433
- // Schema migration v13 → v14 — patterns/tag-data-model.md
5433
+ // Schema migration v13 → v14 — docs/contracts/tag-data-model.md
5434
5434
  // ---------------------------------------------------------------------------
5435
5435
 
5436
5436
  describe("schema migration v13 → v14", async () => {
@@ -5809,7 +5809,7 @@ describe("schema migration v15 → v16", async () => {
5809
5809
  });
5810
5810
 
5811
5811
  // ---------------------------------------------------------------------------
5812
- // Tag-scope auth post-v14 — patterns/tag-scoped-tokens.md
5812
+ // Tag-scope auth post-v14 — docs/contracts/tag-scoped-tokens.md
5813
5813
  // ---------------------------------------------------------------------------
5814
5814
 
5815
5815
  describe("tag-scope auth (post-v14 hierarchy)", async () => {
@@ -5824,7 +5824,7 @@ describe("tag-scope auth (post-v14 hierarchy)", async () => {
5824
5824
  });
5825
5825
 
5826
5826
  it("orphan sub-tag fallback: token for `health` still sees `#health/food` even with no declared hierarchy", async () => {
5827
- // Per patterns/tag-scoped-tokens.md §Storage details, the auth check
5827
+ // Per docs/contracts/tag-scoped-tokens.md §Storage details, the auth check
5828
5828
  // also splits on '/' and matches the root verbatim against the raw
5829
5829
  // allowlist. This survives the v14 source-of-truth swap because the
5830
5830
  // fallback lives in src/tag-scope.ts, not in the resolver.
package/core/src/mcp.ts CHANGED
@@ -1408,7 +1408,7 @@ Write-attribution (vault#298): every result carries \`createdBy\`/\`createdVia\`
1408
1408
  {
1409
1409
  name: "update-tag",
1410
1410
  requiredVerb: "write",
1411
- description: "Create or update a tag's identity row: description, indexed-field schemas, relationship-vocabulary map, and hierarchy parents. If the tag doesn't exist, it's created. Fields are merged (new keys added, existing keys replaced); relationships and parent_names are replaced wholesale when provided. Pass null for fields/relationships/parent_names to clear that column. See parachute-patterns/patterns/tag-data-model.md.",
1411
+ description: "Create or update a tag's identity row: description, indexed-field schemas, relationship-vocabulary map, and hierarchy parents. If the tag doesn't exist, it's created. Fields are merged (new keys added, existing keys replaced); relationships and parent_names are replaced wholesale when provided. Pass null for fields/relationships/parent_names to clear that column. See parachute-vault/docs/contracts/tag-data-model.md.",
1412
1412
  inputSchema: {
1413
1413
  type: "object",
1414
1414
  properties: {
@@ -41,7 +41,7 @@ CREATE TABLE IF NOT EXISTS notes (
41
41
 
42
42
  -- Tags: first-class identity carrying schema, hierarchy, and typed-link
43
43
  -- declarations. One row per tag; no notes-as-config sidecars for these
44
- -- concerns. See parachute-patterns/patterns/tag-data-model.md.
44
+ -- concerns. See docs/contracts/tag-data-model.md.
45
45
  --
46
46
  -- description — human-readable blurb (markdown).
47
47
  -- fields — JSON: indexed metadata field declarations per
@@ -136,7 +136,7 @@ CREATE TABLE IF NOT EXISTS indexed_fields (
136
136
  -- scoped_tags is a JSON-encoded array of root tag names that constrain the
137
137
  -- token's effective access (intersection with the scopes column). NULL
138
138
  -- means unscoped — full vault access per scopes. Introduced in v13 per
139
- -- patterns/tag-scoped-tokens.md. Hierarchy expansion is applied at auth
139
+ -- docs/contracts/tag-scoped-tokens.md. Hierarchy expansion is applied at auth
140
140
  -- time via getTagDescendants; the column stores root names only.
141
141
  --
142
142
  -- vault_name (v16) binds the token to a single vault. NULL means the
@@ -443,7 +443,7 @@ export function initSchema(db: Database): void {
443
443
  // Migrate v13 → v14: tag-data-model reshape. Augment `tags` row with
444
444
  // description/fields/relationships/parent_names/timestamps; copy data
445
445
  // from the v6-era tag_schemas sidecar and from `_tags/<name>` config
446
- // notes; drop tag_schemas after copy. See patterns/tag-data-model.md.
446
+ // notes; drop tag_schemas after copy. See docs/contracts/tag-data-model.md.
447
447
  migrateToV14(db);
448
448
 
449
449
  // Migrate v14 → v15: retire the `_schemas/<name>` and `_schema_defaults`
@@ -650,7 +650,7 @@ function migrateToV12(db: Database): void {
650
650
  * (current full-vault behavior); a JSON array of root tag names narrows the
651
651
  * token's access to notes carrying one of those tags or a sub-tag thereof
652
652
  * (hierarchy expansion via getTagDescendants at auth time). See
653
- * parachute-patterns/patterns/tag-scoped-tokens.md.
653
+ * docs/contracts/tag-scoped-tokens.md.
654
654
  */
655
655
  function migrateToV13(db: Database): void {
656
656
  if (hasTable(db, "tokens") && !hasColumn(db, "tokens", "scoped_tags")) {
@@ -659,7 +659,7 @@ function migrateToV13(db: Database): void {
659
659
  }
660
660
 
661
661
  /**
662
- * Migrate v13 → v14: tag-data-model reshape (patterns/tag-data-model.md).
662
+ * Migrate v13 → v14: tag-data-model reshape (docs/contracts/tag-data-model.md).
663
663
  *
664
664
  * Augments the `tags` table with five new columns and one timestamp pair,
665
665
  * then copies pre-existing data from two notes-as-config sidecars:
@@ -9,7 +9,7 @@
9
9
  * History: pre-v14 vaults stored hierarchy in notes-as-config at
10
10
  * `_tags/<name>`. The v14 migration (see core/src/schema.ts:migrateToV14)
11
11
  * lifts those parent declarations onto the tags row and the resolver here
12
- * was swapped accordingly. See parachute-patterns/patterns/tag-data-model.md.
12
+ * was swapped accordingly. See docs/contracts/tag-data-model.md.
13
13
  *
14
14
  * Resolution model:
15
15
  * - Lazy: built on first access, cached on the store.
@@ -4,7 +4,7 @@
4
4
  * Each tag row carries: human-readable description, indexed metadata field
5
5
  * declarations (`fields`), typed-link relationship declarations
6
6
  * (`relationships`), and the hierarchy parent list (`parent_names`).
7
- * See parachute-patterns/patterns/tag-data-model.md.
7
+ * See docs/contracts/tag-data-model.md.
8
8
  *
9
9
  * This module retains the historical `tag-schemas` filename and exports
10
10
  * (`TagSchema`, `listTagSchemas`, `getTagSchema`, `upsertTagSchema`,
@@ -51,7 +51,7 @@ export interface TagFieldSchema {
51
51
  * `{ target_tag, cardinality }` declaration — but `relationships` is now an
52
52
  * opaque vocabulary map (see `TagRelationshipMap` / `validateRelationships`),
53
53
  * so this is one valid value shape among many, not a required one.
54
- * See patterns/tag-data-model.md §Relationships.
54
+ * See docs/contracts/tag-data-model.md §Relationships.
55
55
  */
56
56
  export type TagRelCardinality = "one" | "optional" | "many" | "many-required";
57
57
 
package/core/src/types.ts CHANGED
@@ -406,7 +406,7 @@ export interface Store {
406
406
 
407
407
  // Tag records — full v14 identity row (description + fields + typed
408
408
  // relationships + parent_names + timestamps). See
409
- // parachute-patterns/patterns/tag-data-model.md.
409
+ // docs/contracts/tag-data-model.md.
410
410
  listTagRecords(): Promise<TagRecord[]>;
411
411
  getTagRecord(tag: string): Promise<TagRecord | null>;
412
412
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openparachute/vault",
3
- "version": "0.6.5-rc.11",
3
+ "version": "0.6.5-rc.12",
4
4
  "description": "Agent-native knowledge graph. Notes, tags, links over MCP.",
5
5
  "module": "src/cli.ts",
6
6
  "type": "module",
package/src/admin-spa.ts CHANGED
@@ -8,7 +8,8 @@
8
8
  *
9
9
  * Per-vault mount (vault#252): the SPA lives under `/vault/<name>/admin/*`
10
10
  * rather than the origin-rooted `/admin/*` it shipped with. The hub only
11
- * proxies `/vault/<name>/*` paths (per parachute-patterns/module-protocol),
11
+ * proxies `/vault/<name>/*` paths (per the module protocol
12
+ * https://github.com/ParachuteComputer/parachute-hub/blob/main/docs/contracts/module-protocol.md),
12
13
  * so an origin-rooted SPA is unreachable through the hub. Asset URLs are
13
14
  * relative (Vite `base: "./"`), so the same bundle works at any mount
14
15
  * point — no rebuild per vault.
package/src/auth.ts CHANGED
@@ -123,7 +123,7 @@ export interface AuthResult {
123
123
  * Tag-allowlist (root tag names) for tag-scoped tokens. NULL = unscoped
124
124
  * (current full-vault behavior). Hub-issued JWTs and legacy YAML keys
125
125
  * always have null — tag scoping is a per-token vault-DB attribute, not
126
- * an OAuth-claim concern. See patterns/tag-scoped-tokens.md.
126
+ * an OAuth-claim concern. See docs/contracts/tag-scoped-tokens.md.
127
127
  */
128
128
  scoped_tags: string[] | null;
129
129
  /**
@@ -10,7 +10,7 @@
10
10
  * cursor captured at the *start* of the previous cycle. Vault writes are
11
11
  * HTTP-mediated and don't surface to filesystem watchers (the bun:sqlite DB
12
12
  * is opaque), so polling on `updated_at >= cursor` is the simplest robust
13
- * detection. See `parachute-patterns/cookbook/vault-portable-export.md`.
13
+ * detection. See `docs/contracts/vault-portable-export.md`.
14
14
  */
15
15
 
16
16
  import { ensureGitAvailable } from "./git-preflight.ts";
package/src/routes.ts CHANGED
@@ -2089,7 +2089,7 @@ export async function handleTags(
2089
2089
 
2090
2090
  // PUT /tags/:name — upsert tag identity row. Body accepts any combination
2091
2091
  // of { description, fields, relationships, parent_names }; omitted keys
2092
- // are preserved, explicit null clears. See patterns/tag-data-model.md.
2092
+ // are preserved, explicit null clears. See docs/contracts/tag-data-model.md.
2093
2093
  if (req.method === "PUT") {
2094
2094
  // Canonical-bare-tag guard (vault#XXX): normalize the upserted tag NAME so
2095
2095
  // the existing-field merge read (store.getTagSchema below) and the upsert
@@ -2114,7 +2114,7 @@ export async function handleTags(
2114
2114
  * the MCP `update-tag` tool relies on (omitted keys preserved). The
2115
2115
  * Schema editor (vault#283) sends the full map + `replace_fields: true`
2116
2116
  * so removing a field row actually deletes the field. See
2117
- * patterns/tag-data-model.md.
2117
+ * docs/contracts/tag-data-model.md.
2118
2118
  */
2119
2119
  replace_fields?: unknown;
2120
2120
  };
@@ -2211,7 +2211,7 @@ export async function handleTags(
2211
2211
  // Tag-scoped tokens reference root tags by name; deleting a referenced
2212
2212
  // tag would silently orphan the token's allowlist. Fail closed (409)
2213
2213
  // and name the offending tokens so the operator can revoke or re-mint
2214
- // before retrying. patterns/tag-scoped-tokens.md §Dependencies.
2214
+ // before retrying. docs/contracts/tag-scoped-tokens.md §Dependencies.
2215
2215
  const referenced_by = findTokensReferencingTag(store.db, tagName);
2216
2216
  if (referenced_by.length > 0) {
2217
2217
  return json(
@@ -1631,7 +1631,7 @@ describe("scope enforcement on /api/*", () => {
1631
1631
  expect(res.status).toBe(401);
1632
1632
  });
1633
1633
 
1634
- // ----- tag-scoped tokens (patterns/tag-scoped-tokens.md) -----------------
1634
+ // ----- tag-scoped tokens (docs/contracts/tag-scoped-tokens.md) -----------------
1635
1635
 
1636
1636
  /**
1637
1637
  * Mint a tag-scoped token. Mirrors `mintToken` above but threads
@@ -1745,7 +1745,7 @@ describe("scope enforcement on /api/*", () => {
1745
1745
  });
1746
1746
 
1747
1747
  // ----- Q6: orphan-sub-tag fail-open ------------------------------------
1748
- // patterns/tag-scoped-tokens.md §Storage: when a sub-tag has no declared
1748
+ // docs/contracts/tag-scoped-tokens.md §Storage: when a sub-tag has no declared
1749
1749
  // schema, the string-form root authorizes. Token allowlisted for `health`
1750
1750
  // must see `#health/food` even when no `_tags/health/food` schema exists.
1751
1751
 
package/src/routing.ts CHANGED
@@ -856,7 +856,7 @@ export async function route(
856
856
 
857
857
  const apiPath = apiMatch[1] ?? "";
858
858
 
859
- // Tag-scoped tokens (patterns/tag-scoped-tokens.md): expand the token's
859
+ // Tag-scoped tokens (docs/contracts/tag-scoped-tokens.md): expand the token's
860
860
  // root-tag allowlist into `{root} ∪ descendants(root)` once per request,
861
861
  // so handlers can intersect against the note's tag set without re-walking
862
862
  // the `_tags/<name>` hierarchy on every check. `tagScope.allowed` is null
package/src/tag-scope.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  /**
2
- * Tag-scope enforcement for tag-scoped tokens (patterns/tag-scoped-tokens.md).
2
+ * Tag-scope enforcement for tag-scoped tokens (docs/contracts/tag-scoped-tokens.md).
3
3
  *
4
4
  * A token's `scoped_tags` allowlist narrows its effective access to notes
5
5
  * carrying one of the allowlisted tags or a sub-tag thereof. The expansion
6
6
  * to descendants happens via the per-vault `_tags/<name>` config-note
7
7
  * hierarchy (see core/src/tag-hierarchy.ts).
8
8
  *
9
- * Auth check pseudocode (from patterns/tag-scoped-tokens.md):
9
+ * Auth check pseudocode (from docs/contracts/tag-scoped-tokens.md):
10
10
  *
11
11
  * if (!hasScope(token, ...)) return forbidden();
12
12
  * if (token.scoped_tags === null) return ok(); // unscoped
@@ -38,7 +38,7 @@ export async function expandTokenTagScope(
38
38
 
39
39
  /**
40
40
  * Return true iff the note's tag set intersects the expanded allowlist OR
41
- * — fail-open per patterns/tag-scoped-tokens.md §Storage — any of the
41
+ * — fail-open per docs/contracts/tag-scoped-tokens.md §Storage — any of the
42
42
  * note's tags has a string-form root inside `rawRoots`. The string-form
43
43
  * fallback covers the orphan-sub-tag case: a token allowlisted for
44
44
  * `health` should still see `#health/food` even when no `_tags/health/food`
@@ -54,7 +54,7 @@ export interface Token {
54
54
  * access is the intersection of `scopes` and notes carrying one of these
55
55
  * tags or a sub-tag thereof (hierarchy expansion via getTagDescendants).
56
56
  * NULL = unscoped, full vault access per `scopes`. See
57
- * patterns/tag-scoped-tokens.md.
57
+ * docs/contracts/tag-scoped-tokens.md.
58
58
  */
59
59
  scoped_tags: string[] | null;
60
60
  /**
@@ -301,7 +301,7 @@ export function markMcpMintLedgerRevoked(
301
301
  * Returns display ID + label pairs (no token-hash exposure) so error
302
302
  * envelopes can name the offending tokens for the operator. The match is
303
303
  * exact on the root name — `scoped_tags` only ever stores roots per
304
- * patterns/tag-scoped-tokens.md.
304
+ * docs/contracts/tag-scoped-tokens.md.
305
305
  */
306
306
  export function findTokensReferencingTag(
307
307
  db: Database,
package/src/vault.test.ts CHANGED
@@ -1093,7 +1093,7 @@ describe("scoped MCP wrapper", async () => {
1093
1093
  close();
1094
1094
  });
1095
1095
 
1096
- // -- tag-scoped MCP wrappers (patterns/tag-scoped-tokens.md) ------------
1096
+ // -- tag-scoped MCP wrappers (docs/contracts/tag-scoped-tokens.md) ------------
1097
1097
  //
1098
1098
  // These pin the behavior of `applyTagScopeWrappers` in mcp-tools.ts: each
1099
1099
  // wrapped tool's execute() honors the auth's scoped_tags allowlist. The