@profile-as-code/core 0.0.0-stage → 0.1.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.
Files changed (67) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +80 -2
  3. package/dist/byte-order.d.ts +17 -0
  4. package/dist/byte-order.d.ts.map +1 -0
  5. package/dist/byte-order.js +19 -0
  6. package/dist/byte-order.js.map +1 -0
  7. package/dist/compile.d.ts +40 -0
  8. package/dist/compile.d.ts.map +1 -0
  9. package/dist/compile.js +120 -0
  10. package/dist/compile.js.map +1 -0
  11. package/dist/completeness.d.ts +81 -0
  12. package/dist/completeness.d.ts.map +1 -0
  13. package/dist/completeness.js +300 -0
  14. package/dist/completeness.js.map +1 -0
  15. package/dist/drift.d.ts +43 -0
  16. package/dist/drift.d.ts.map +1 -0
  17. package/dist/drift.js +277 -0
  18. package/dist/drift.js.map +1 -0
  19. package/dist/generated/platforms.gen.d.ts +26 -0
  20. package/dist/generated/platforms.gen.d.ts.map +1 -0
  21. package/dist/generated/platforms.gen.js +185 -0
  22. package/dist/generated/platforms.gen.js.map +1 -0
  23. package/dist/generated/schema.zod.d.ts +575 -0
  24. package/dist/generated/schema.zod.d.ts.map +1 -0
  25. package/dist/generated/schema.zod.js +31 -0
  26. package/dist/generated/schema.zod.js.map +1 -0
  27. package/dist/geo.d.ts +34 -0
  28. package/dist/geo.d.ts.map +1 -0
  29. package/dist/geo.js +601 -0
  30. package/dist/geo.js.map +1 -0
  31. package/dist/index.d.ts +32 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +25 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/ledger.d.ts +238 -0
  36. package/dist/ledger.d.ts.map +1 -0
  37. package/dist/ledger.js +429 -0
  38. package/dist/ledger.js.map +1 -0
  39. package/dist/load.d.ts +51 -0
  40. package/dist/load.d.ts.map +1 -0
  41. package/dist/load.js +129 -0
  42. package/dist/load.js.map +1 -0
  43. package/dist/media.d.ts +82 -0
  44. package/dist/media.d.ts.map +1 -0
  45. package/dist/media.js +170 -0
  46. package/dist/media.js.map +1 -0
  47. package/dist/paste-source.d.ts +14 -0
  48. package/dist/paste-source.d.ts.map +1 -0
  49. package/dist/paste-source.js +17 -0
  50. package/dist/paste-source.js.map +1 -0
  51. package/dist/registry.d.ts +76 -0
  52. package/dist/registry.d.ts.map +1 -0
  53. package/dist/registry.js +210 -0
  54. package/dist/registry.js.map +1 -0
  55. package/dist/selection.d.ts +98 -0
  56. package/dist/selection.d.ts.map +1 -0
  57. package/dist/selection.js +242 -0
  58. package/dist/selection.js.map +1 -0
  59. package/dist/structured-data.d.ts +82 -0
  60. package/dist/structured-data.d.ts.map +1 -0
  61. package/dist/structured-data.js +265 -0
  62. package/dist/structured-data.js.map +1 -0
  63. package/dist/types.d.ts +320 -0
  64. package/dist/types.d.ts.map +1 -0
  65. package/dist/types.js +34 -0
  66. package/dist/types.js.map +1 -0
  67. package/package.json +59 -3
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Christian Luciani
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,81 @@
1
- # Temporary Holding Version
1
+ # @profile-as-code/core
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The core engine of **profile-as-code** (M1): load a profile-data workspace,
4
+ validate it against the contract, and run the drift / GEO / compile layers.
5
+
6
+ The contract source is `schema/profile.schema.json` (D3) — Zod schemas are
7
+ **generated** from it, never hand-written (see `scripts/gen-zod.ts`).
8
+
9
+ ## API
10
+
11
+ ```ts
12
+ import { load, driftCheck, geoCheck, compile } from '@profile-as-code/core';
13
+
14
+ const profile = load('/path/to/profile-data'); // parse content/*.yaml + validate (FAIL on schema, D12)
15
+ const issues = driftCheck(profile); // Issue[] — WARN only, never throws (D12)
16
+ const geo = geoCheck(profile); // GeoReport — 0..100 score, not a gate (§8)
17
+ const slice = compile(profile, 'linkedin', 'es'); // PlatformProfile — language-aware renderer slice
18
+ ```
19
+
20
+ - **`load(workspace)`** — reads `content/*.yaml` (file name = root property:
21
+ `bios.yaml` → `bios[]`, `identity.yaml` → `identity`, …), merges, validates
22
+ against the generated Zod, normalizes (collections default to `[]`).
23
+ Throws `LoadError` (workspace problems) / `SchemaValidationError` (contract
24
+ violations with one precise `path` + `message` per error — the fail-on-schema
25
+ side of D12). Unknown files fail validation via `additionalProperties: false`.
26
+ - **`driftCheck(profile)`** — factual contradictions across surfaces, all WARN
27
+ (users may have different intents per platform, but they must SEE them):
28
+ uncanonicalized platform URLs, `start > end`, duplicated bios/headlines,
29
+ same role/org with different dates, same title with different year/venue,
30
+ ORCID conflicts/mismatches vs `identity.links`, active entries without a
31
+ curated base, unknown active platform keys, skill level conflicts.
32
+ *Out of scope (v1):* free-text mining of bio prose, DOI resolvability
33
+ (network), the variants store (only the platform+lang base is validated).
34
+ - **`geoCheck(profile)`** — weighted report (14 checks since v1.10, 0..100):
35
+ **fact consistency** (conflicts + per-platform bio/headline coverage),
36
+ **structured-data readiness** (`schema.org` Person JSON-LD with `sameAs`,
37
+ ready for the web renderer), **semantic associations** (the 3–5
38
+ `positioning.geo.semanticAssociations` anchors and how well each is
39
+ reinforced across statement/skills/keywords/projects), **citability**
40
+ (DOI/ORCID on publications, `evidence[]` on projects) plus the v1.10 citability
41
+ codes — canonical definition, FAQ coverage, freshness, sameAs coverage, name
42
+ collision. Missing data is neutral for the fact checks (never penalized);
43
+ readiness/citability checks (structured data, semantic anchors, the v1.10 codes)
44
+ can score 0 when the signal is absent — a reported gap, not a penalty (SPEC §8).
45
+ - **`compile(profile, platform, lang)`** — facts pass through untouched
46
+ (identical on every platform = GEO-citable); text (bios, headlines, statement,
47
+ audiences, citationsGoal) is filtered by platform+lang and falls back to `en`
48
+ (always required by the schema), recording each fallback in `fallbacks`.
49
+
50
+ ## Contract fidelity (generator)
51
+
52
+ `scripts/gen-zod.ts` regenerates `src/generated/schema.zod.ts` from
53
+ `schema/profile.schema.json`. Because `json-schema-to-zod@2.8.1` does not resolve
54
+ `$defs`, ignore `propertyNames`, and bloats `oneOf`, the generator:
55
+
56
+ 1. inlines `#/$defs/*` references (fails on unknown/circular defs),
57
+ 2. rewrites the lone `oneOf` (`check.value`) to `anyOf` (clean `z.union`),
58
+ 3. materializes `langMap.en` as a real property (keeps `required: ['en']`), and
59
+ 4. wraps every langMap with a `langMapKeys` superRefine enforcing the
60
+ `en/es/fr` key enum (`propertyNames`).
61
+
62
+ Safety nets fail CI loudly if the output ever contains `z.any()` or loses the
63
+ langMap guard.
64
+
65
+ ## Test
66
+
67
+ ```sh
68
+ pnpm install
69
+ pnpm test # vitest (load, drift, geo, compile)
70
+ pnpm build # strict tsc → dist/
71
+ pnpm gen:zod # regenerate src/generated/schema.zod.ts (must produce no diff)
72
+ ```
73
+
74
+ Fixtures live in `tests/fixtures/`: `minimal/` (drift-free, GEO-rich — the
75
+ reference fixture) and `drifting/` (schema-valid but contradicts itself on
76
+ every drift rule).
77
+
78
+ Requires Node ≥ 22 (the `engines` field of this manifest). Full source and docs
79
+ live in the [public repository](https://github.com/ChristianLuciani/profile-as-code) —
80
+ `scripts/gen-zod.ts` and `tests/fixtures/` above are package-relative paths there
81
+ (`packages/core/…`); the contract source is the repository's `schema/profile.schema.json`.
@@ -0,0 +1,17 @@
1
+ /**
2
+ * byte-order.ts — the ONE home of every text-order comparison in the engine
3
+ * (D19 byte-parity, issue #305).
4
+ *
5
+ * The comparator is UTF-16 code UNITS — JS native `a < b` string order:
6
+ * deterministic on every environment, ICU-free. It is NOT codepoint order on
7
+ * the astral edge, and SPEC §15.3 names that choice exactly. A `localeCompare`
8
+ * call-site would depend on the ICU build and can tie (`0`), leaking
9
+ * environment-specific order through a stable sort — the class #257 fixed for
10
+ * the featured slice and #305 closes workspace-wide.
11
+ *
12
+ * The discriminating probe (`apple` vs `APPLE`: ICU collation reads lowercase
13
+ * first, code units read uppercase first) lives at
14
+ * packages/core/tests/byte-order.test.ts.
15
+ */
16
+ export declare function byteOrder(a: string, b: string): number;
17
+ //# sourceMappingURL=byte-order.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"byte-order.d.ts","sourceRoot":"","sources":["../src/byte-order.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAEtD"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * byte-order.ts — the ONE home of every text-order comparison in the engine
3
+ * (D19 byte-parity, issue #305).
4
+ *
5
+ * The comparator is UTF-16 code UNITS — JS native `a < b` string order:
6
+ * deterministic on every environment, ICU-free. It is NOT codepoint order on
7
+ * the astral edge, and SPEC §15.3 names that choice exactly. A `localeCompare`
8
+ * call-site would depend on the ICU build and can tie (`0`), leaking
9
+ * environment-specific order through a stable sort — the class #257 fixed for
10
+ * the featured slice and #305 closes workspace-wide.
11
+ *
12
+ * The discriminating probe (`apple` vs `APPLE`: ICU collation reads lowercase
13
+ * first, code units read uppercase first) lives at
14
+ * packages/core/tests/byte-order.test.ts.
15
+ */
16
+ export function byteOrder(a, b) {
17
+ return a < b ? -1 : a > b ? 1 : 0;
18
+ }
19
+ //# sourceMappingURL=byte-order.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"byte-order.js","sourceRoot":"","sources":["../src/byte-order.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,SAAS,CAAC,CAAS,EAAE,CAAS;IAC5C,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACpC,CAAC"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * compile(profile, platform, lang) — the language-aware slice a renderer consumes
3
+ * (SPEC §7). Facts (identity, experience, projects, publications, skills) pass
4
+ * through UNTOUCHED — they are language-independent and identical on every
5
+ * platform (that is what makes them GEO-citable). Only text (bios, headlines,
6
+ * statement, audiences, citationsGoal) is filtered by platform+lang, falling back
7
+ * to `en` when the requested lang is absent (EN is always required by the schema).
8
+ *
9
+ * The platform set is the single `PLATFORMS` const from types.js (generated from
10
+ * the schema enum) — never duplicated here.
11
+ */
12
+ import { type CompletenessPolicy } from "./completeness.js";
13
+ import { type SelectionPolicy } from "./selection.js";
14
+ import type { Lang, Platform, PlatformProfile, Profile } from "./types.js";
15
+ /**
16
+ * D22/§21: the compile policy — the completeness policy it always took, plus the
17
+ * optional selection policy. ADDITIVE: `CompletenessPolicy` callers keep
18
+ * compiling unchanged, and a policy without `selection` produces a slice
19
+ * byte-identical to before this feature existed (the compatibility guarantee,
20
+ * asserted by a golden test).
21
+ */
22
+ export type CompilePolicy = CompletenessPolicy & {
23
+ selection?: SelectionPolicy;
24
+ /** D22/§21: the view NAME to resolve (a declared view may target a platform
25
+ * other than its own name, e.g. `--view film` → `github-readme`). Defaults to
26
+ * the platform, which is the implicit view (`pasco build linkedin`). */
27
+ view?: string;
28
+ };
29
+ /**
30
+ * Compile a PlatformProfile for one platform and language.
31
+ *
32
+ * @param policy the workspace compile policy (issue #102 + D22): the completeness
33
+ * policy, and optionally the fact-selection policy. The CLI passes `.pascorc`'s
34
+ * so the artifact carries the SAME number the workspace reports and the SAME
35
+ * view it resolved; omitted ⇒ no selection, shipped completeness default.
36
+ * @throws TypeError — `platform`/`lang` not in the contract enum (a renderer bug,
37
+ * not a user-data problem).
38
+ */
39
+ export declare function compile(profile: Profile, platform: Platform, lang?: Lang, policy?: CompilePolicy): PlatformProfile;
40
+ //# sourceMappingURL=compile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../src/compile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,KAAK,kBAAkB,EAAgB,MAAM,mBAAmB,CAAC;AAE1E,OAAO,EAAE,KAAK,eAAe,EAAe,MAAM,gBAAgB,CAAC;AAEnE,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAK3E;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,kBAAkB,GAAG;IAC/C,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B;;6EAEyE;IACzE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,OAAO,CACrB,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,QAAQ,EAClB,IAAI,GAAE,IAAW,EACjB,MAAM,CAAC,EAAE,aAAa,GACrB,eAAe,CAyGjB"}
@@ -0,0 +1,120 @@
1
+ /**
2
+ * compile(profile, platform, lang) — the language-aware slice a renderer consumes
3
+ * (SPEC §7). Facts (identity, experience, projects, publications, skills) pass
4
+ * through UNTOUCHED — they are language-independent and identical on every
5
+ * platform (that is what makes them GEO-citable). Only text (bios, headlines,
6
+ * statement, audiences, citationsGoal) is filtered by platform+lang, falling back
7
+ * to `en` when the requested lang is absent (EN is always required by the schema).
8
+ *
9
+ * The platform set is the single `PLATFORMS` const from types.js (generated from
10
+ * the schema enum) — never duplicated here.
11
+ */
12
+ import { completeness } from "./completeness.js";
13
+ import { normalizeProfile } from "./load.js";
14
+ import { selectFacts } from "./selection.js";
15
+ import { structuredData } from "./structured-data.js";
16
+ import { PLATFORMS } from "./types.js";
17
+ const LANG_IDS = ["en", "es", "fr"];
18
+ /**
19
+ * Compile a PlatformProfile for one platform and language.
20
+ *
21
+ * @param policy the workspace compile policy (issue #102 + D22): the completeness
22
+ * policy, and optionally the fact-selection policy. The CLI passes `.pascorc`'s
23
+ * so the artifact carries the SAME number the workspace reports and the SAME
24
+ * view it resolved; omitted ⇒ no selection, shipped completeness default.
25
+ * @throws TypeError — `platform`/`lang` not in the contract enum (a renderer bug,
26
+ * not a user-data problem).
27
+ */
28
+ export function compile(profile, platform, lang = "en", policy) {
29
+ if (!PLATFORMS.includes(platform)) {
30
+ throw new TypeError(`compile: unknown platform "${String(platform)}" (allowed: ${PLATFORMS.join(", ")})`);
31
+ }
32
+ if (!LANG_IDS.includes(lang)) {
33
+ throw new TypeError(`compile: unknown language "${String(lang)}" (allowed: ${LANG_IDS.join(", ")})`);
34
+ }
35
+ const fallbacks = [];
36
+ const langText = (langMap, path) => {
37
+ // Issue #84 (option (a) ratified): an empty-string value counts as ABSENT —
38
+ // nobody can display "" and a populated workspace is byte-identical; only
39
+ // previously-degenerate output changes. `|| undefined` (not just `??`) so
40
+ // emptiness falls through at BOTH lookup levels (lang-specific and en).
41
+ const direct = langMap?.[lang] || undefined;
42
+ if (direct !== undefined)
43
+ return direct;
44
+ const en = langMap?.en || undefined;
45
+ if (en !== undefined) {
46
+ fallbacks.push(path);
47
+ return en;
48
+ }
49
+ return undefined;
50
+ };
51
+ const positioning = profile.positioning;
52
+ const audiences = {};
53
+ for (const [name, map] of Object.entries(positioning?.audiences ?? {})) {
54
+ const text = langText(map, `positioning.audiences.${name}`);
55
+ if (text !== undefined)
56
+ audiences[name] = text;
57
+ }
58
+ // Brand blocks (v1.1/§17): text is lang-filtered (falling back to en), facts
59
+ // (urls, dates, status) pass through untouched.
60
+ const brandAudiences = {};
61
+ for (const [name, map] of Object.entries(profile.audiences ?? {})) {
62
+ const text = langText(map, `audiences.${name}`);
63
+ if (text !== undefined)
64
+ brandAudiences[name] = text;
65
+ }
66
+ const platformChecks = (profile.checks ?? []).filter((c) => c.platform === "all" || c.platform === platform);
67
+ // D22/§21 — the view. `facts` is what renderers may see; `profile` (unfiltered)
68
+ // is what completeness scores. The split is the contract, not an accident:
69
+ // - structuredData is built from the FILTERED profile, or the JSON-LD would
70
+ // claim entities the render never shows;
71
+ // - completeness stays on the UNFILTERED profile, because §20 scores the
72
+ // WORKSPACE, not a view (hiding half the SSOT must not lower the number).
73
+ const selection = policy?.selection === undefined
74
+ ? undefined
75
+ : selectFacts(profile, policy.view ?? platform, platform, lang, policy.selection);
76
+ const facts = selection?.profile ?? profile;
77
+ return {
78
+ platform,
79
+ lang,
80
+ flavor: profile.flavor ?? "person",
81
+ // facts — untouched
82
+ identity: facts.identity,
83
+ experience: facts.experience ?? [],
84
+ projects: facts.projects ?? [],
85
+ publications: facts.publications ?? [],
86
+ skills: facts.skills ?? [],
87
+ socialHandles: profile.socialHandles ?? [],
88
+ newsletter: profile.newsletter,
89
+ podcast: profile.podcast,
90
+ press: profile.press ?? [],
91
+ brandAudiences,
92
+ // language-aware text
93
+ bios: (profile.bios ?? []).filter((b) => b.platform === platform && b.lang === lang),
94
+ headlines: (profile.headlines ?? []).filter((h) => h.platform === platform && h.lang === lang),
95
+ statement: langText(positioning?.statement, "positioning.statement"),
96
+ audiences,
97
+ citationsGoal: langText(positioning?.geo?.citationsGoal, "positioning.geo.citationsGoal"),
98
+ keywords: {
99
+ seo: profile.keywords?.seo?.[platform] ?? [],
100
+ geo: profile.keywords?.geo ?? {},
101
+ },
102
+ checks: platformChecks,
103
+ // Evidence registry (v1.4/D17, §18) — facts, pass through untouched.
104
+ credentials: facts.credentials ?? [],
105
+ artifacts: facts.artifacts ?? [],
106
+ ...(profile.active?.[platform] ? { active: profile.active[platform] } : {}),
107
+ // v1.6 (#103): entity structured-data identity — assembled ONCE here, so no
108
+ // renderer re-derives `@id`/`sameAs` (SPEC §15.4, the §18.9 precedent).
109
+ structuredData: structuredData(facts, lang),
110
+ // Issue #102: the completeness report travels WITH the slice (like
111
+ // structuredData) so a renderer emits the number verbatim and never
112
+ // recomputes it. `policy` is the workspace's `.pascorc` policy when the CLI
113
+ // passes it, so the artifact and `pasco status --completeness` agree
114
+ // (review of #205, finding 3); omitted ⇒ the shipped default.
115
+ completeness: completeness(normalizeProfile(profile), policy),
116
+ ...(selection === undefined ? {} : { selection: selection.report }),
117
+ fallbacks,
118
+ };
119
+ }
120
+ //# sourceMappingURL=compile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compile.js","sourceRoot":"","sources":["../src/compile.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * completeness(profile, policy) — the profile-completeness checklist (issue #102).
3
+ *
4
+ * The engine computes; the workspace stores only the POLICY (weights, target,
5
+ * expected langs) and a GENERATED view — never the percentage (a derived number
6
+ * that would drift). Same contract as `geoCheck` (SPEC §7): pure, deterministic,
7
+ * offline, no network.
8
+ *
9
+ * Five signals, each an already-present engine fact (the set ratified 2026-10-07):
10
+ *
11
+ * presence blocks vs the flavor's expected set (flavor + top-level props)
12
+ * langCoverage en/es/fr coverage of every langMap (load/schema)
13
+ * geoAnchors semantic associations reinforced elsewhere (geoCheck's surface)
14
+ * evidence claims that carry a source + registry entries (`evidenceItem`)
15
+ * deployment share of trackable units with a ledger record (`ledger`)
16
+ *
17
+ * Policy is parameterized (dev-methodology §3): a user says "my target is the
18
+ * GitHub milestone set, not everything" by dropping an item (weight 0) — a
19
+ * parameter, never a code path. No weights are hardcoded outside the catalog.
20
+ */
21
+ import type { LoadedProfile } from "./types.js";
22
+ /** One scored signal the engine can compute. */
23
+ export type CompletenessSignal = "presence" | "langCoverage" | "geoAnchors" | "evidence" | "deployment";
24
+ /** A catalog entry: the engine-owned definition of one completeness item. */
25
+ export interface CompletenessCatalogItem {
26
+ id: CompletenessSignal;
27
+ label: string;
28
+ /** The profile block(s) this item draws on — the issue's "source" column. */
29
+ block: string;
30
+ /** The shipped, documented default weight (normalized when the score is computed). */
31
+ weight: number;
32
+ }
33
+ /**
34
+ * The item catalog — the engine's knowledge of what "complete" can mean.
35
+ * `weight` here is the DEFAULT policy's weight; a policy overrides per id.
36
+ */
37
+ export declare const COMPLETENESS_CATALOG: readonly CompletenessCatalogItem[];
38
+ /** The policy: what a workspace is being measured against. */
39
+ export interface CompletenessPolicy {
40
+ /** Name of the milestone this policy targets (informational, e.g. `github-milestone`). */
41
+ target?: string;
42
+ /** Languages text coverage is measured in (default: en/es/fr). */
43
+ langs?: readonly string[];
44
+ /** Per-item weight override; weight 0 removes the item from the score. */
45
+ items?: readonly {
46
+ id: string;
47
+ weight: number;
48
+ }[];
49
+ }
50
+ /** One computed item: a score in 0..1, its weight, and a short EN detail. */
51
+ export interface CompletenessItemResult {
52
+ id: string;
53
+ label: string;
54
+ block: string;
55
+ score: number;
56
+ weight: number;
57
+ satisfied: boolean;
58
+ detail: string;
59
+ }
60
+ export interface CompletenessReport {
61
+ /** 0..100, weighted mean of the item scores (one decimal, byte-stable). */
62
+ percent: number;
63
+ /** Items with score 1 — the numerator of the `11/18` surface. */
64
+ satisfied: number;
65
+ /** Items considered by the policy (weight > 0). */
66
+ total: number;
67
+ target: string;
68
+ items: CompletenessItemResult[];
69
+ }
70
+ /** The shipped, documented default (overridable per workspace via `.pascorc`). */
71
+ export declare const DEFAULT_COMPLETENESS_POLICY: CompletenessPolicy;
72
+ /**
73
+ * Compute the profile's completeness against a policy.
74
+ *
75
+ * @param profile a `load()`ed (normalized) profile — array blocks are defined.
76
+ * @param policy weights/target/langs; omitted ⇒ {@link DEFAULT_COMPLETENESS_POLICY}.
77
+ */
78
+ export declare function completeness(profile: LoadedProfile, policy?: CompletenessPolicy): CompletenessReport;
79
+ /** `62.5% — 3/5` — the deterministic one-liner the CLI and the renderer share. */
80
+ export declare function completenessLine(report: CompletenessReport): string;
81
+ //# sourceMappingURL=completeness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"completeness.d.ts","sourceRoot":"","sources":["../src/completeness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,gDAAgD;AAChD,MAAM,MAAM,kBAAkB,GAC1B,UAAU,GACV,cAAc,GACd,YAAY,GACZ,UAAU,GACV,YAAY,CAAC;AAEjB,6EAA6E;AAC7E,MAAM,WAAW,uBAAuB;IACtC,EAAE,EAAE,kBAAkB,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,6EAA6E;IAC7E,KAAK,EAAE,MAAM,CAAC;IACd,sFAAsF;IACtF,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,eAAO,MAAM,oBAAoB,EAAE,SAAS,uBAAuB,EAMzD,CAAC;AAEX,8DAA8D;AAC9D,MAAM,WAAW,kBAAkB;IACjC,0FAA0F;IAC1F,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kEAAkE;IAClE,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B,0EAA0E;IAC1E,KAAK,CAAC,EAAE,SAAS;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACnD;AAED,6EAA6E;AAC7E,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,2EAA2E;IAC3E,OAAO,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,sBAAsB,EAAE,CAAC;CACjC;AAED,kFAAkF;AAClF,eAAO,MAAM,2BAA2B,EAAE,kBAGzC,CAAC;AAmOF;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,aAAa,EACtB,MAAM,GAAE,kBAAgD,GACvD,kBAAkB,CA6CpB;AAED,kFAAkF;AAClF,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAEnE"}