@nanopm/cli 0.1.2 → 0.1.5

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/dist/index.js CHANGED
@@ -863,22 +863,14 @@ var CHAINED = {
863
863
  // (`mapping_completion_hook`): the opportunity map follows the market read below, and
864
864
  // the founder confirms what the scan proposed on the card while it is drawn.
865
865
  //
866
- // **`direction` is queued here rather than after the market, and still reads it.** The
867
- // daemon runs one job at a time and `claim_job` takes them `order by created_at`, so a
868
- // direction queued the moment the onboarding ends is not *started* until the market —
869
- // queued a hair earlier, in this array's own order — has finished and written the
870
- // competitors its judge needs for the substitution test. What it skips is the queue behind
871
- // `pitch`, which it never needed and which cost it eighty seconds: on dogo-3 (2026-09-24)
872
- // the direction landed at 441s against a founder who reaches the card at about 90, and the
873
- // Vision row said *nothing yet* twenty-five seconds before the crew wrote it. Now ~358s.
874
- //
875
- // It does not close the gap — nothing chained can, while the read itself takes 161s — which
876
- // is why the mission is asked for on the way in and the vision is drawn as *coming*
877
- // (docs/nanopm-56-the-required-set.md). This makes that wait honest rather than shorter
878
- // than it looks.
879
- // Since 2026-09-30 the direction is queued only when the onboarding left the mission or the
880
- // vision empty (`WRITTEN_BY_PARENT`): with both written, it had nothing to fill.
881
- onboard: ["market", "direction"],
866
+ // **The direction crew does not follow an onboarding** (Nicolas, 2026-10-01, docs/ACTIVATION.md
867
+ // row 27). It was queued here, in the background, whenever the read left the mission or the
868
+ // vision empty, and it landed 9 to 24 minutes later — after the market, the map and the
869
+ // solutions — while the card's Direction step, right after the pitch, promised *a few
870
+ // minutes*. Direction now works like the card's other sections: the read writes what the
871
+ // repository states, the card asks the founder for a mission it did not find (`missionAsk`),
872
+ // and the crew runs when the founder asks for it from the business card.
873
+ onboard: ["market"],
882
874
  // A problem list is what solutions hang under, so a fresh list is followed by fresh
883
875
  // solutions. **The problem crew itself is retired** (docs/nanopm-59-opportunity-map.md,
884
876
  // Guillaume, 2026-09-25): the opportunity map replaced it, and nothing queues it any more —
@@ -891,10 +883,8 @@ var CHAINED = {
891
883
  // proposes beside a confirmed pitch and never over it (docs/nanopm-52-pitch-crew.md).
892
884
  //
893
885
  // The direction crew **was** chained here too, for the same reason — a vision rests on who
894
- // else exists — and it moved up to `onboard` on 2026-09-24. It still reads the market: the
895
- // daemon is serial and claims `order by created_at`, so a direction queued at the end of
896
- // the onboarding starts only once the market, queued a hair before it, has finished. All
897
- // it gave up was waiting behind the pitch, which it never read. See the note on `onboard`.
886
+ // else exists — then after `onboard`, and since 2026-10-01 after nothing: see the note on
887
+ // `onboard`.
898
888
  //
899
889
  // Neither follows a personas run the founder started: they pressed one button, for one
900
890
  // crew, with a note meant for it (Guillaume, 2026-09-22). A founder who wants both runs asks
@@ -948,16 +938,6 @@ var ONLY_AFTER = {
948
938
  var NEVER_AFTER = {
949
939
  market: { pitch: ["onboard"] }
950
940
  };
951
- var WRITTEN_BY_PARENT = {
952
- onboard: { direction: ["mission", "vision"] }
953
- };
954
- function stillOwed(skill, next2, written) {
955
- const needs2 = WRITTEN_BY_PARENT[skill] ?? {};
956
- return next2.filter((n) => !needs2[n] || needs2[n].some((topic) => !written.has(topic)));
957
- }
958
- var BACKGROUND_EDGES = {
959
- onboard: ["direction"]
960
- };
961
941
  var AFTER_DEEP = {
962
942
  needs: ["opportunities"]
963
943
  };
@@ -10956,12 +10936,6 @@ async function runDaemon(options = {}) {
10956
10936
  }
10957
10937
  return { daemonId, processed };
10958
10938
  }
10959
- async function topicsWritten(auth, projectId, skill) {
10960
- const topics = [...new Set(Object.values(WRITTEN_BY_PARENT[skill] ?? {}).flat())];
10961
- if (!topics.length) return /* @__PURE__ */ new Set();
10962
- const { data } = await auth.client.from("memory").select("topic").eq("project_id", projectId).in("topic", topics).is("superseded_by", null);
10963
- return new Set((data ?? []).map((r) => r.topic));
10964
- }
10965
10939
  async function chainAfter(auth, job, outcome, log) {
10966
10940
  if (NOT_CHAINED_BY_DAEMON.includes(job.skill)) return;
10967
10941
  if (job.params?.chain === false) return;
@@ -10969,14 +10943,13 @@ async function chainAfter(auth, job, outcome, log) {
10969
10943
  const parent = job.parent_job_id ? await parentOf(auth, job.parent_job_id) : null;
10970
10944
  const own = job.params ?? {};
10971
10945
  const deep = own.depth === "deep" && job.trigger === "hook";
10972
- const next2 = stillOwed(job.skill, daemonQueues(job.skill, 0, parent?.skill ?? null, deep), await topicsWritten(auth, job.project_id, job.skill));
10946
+ const next2 = daemonQueues(job.skill, 0, parent?.skill ?? null, deep);
10973
10947
  if (!next2.length) return;
10974
10948
  if (await chainDepth(auth, job) >= CHAIN_DEPTH_MAX) return;
10975
10949
  const from = deep ? own.repo_path : parent?.params?.repo_path;
10976
10950
  const repo = typeof from === "string" ? { repo_path: from } : null;
10977
10951
  for (const skill of next2) {
10978
- const background = deep || (BACKGROUND_EDGES[job.skill] ?? []).includes(skill);
10979
- const params = { ...repo ?? {}, ...deep ? { depth: "deep" } : {}, ...background ? { background: true } : {} };
10952
+ const params = { ...repo ?? {}, ...deep ? { depth: "deep", background: true } : {} };
10980
10953
  const { data, error } = await auth.client.from("jobs").insert({
10981
10954
  project_id: job.project_id,
10982
10955
  skill,
@@ -11335,9 +11308,12 @@ function validateItem(item) {
11335
11308
  }
11336
11309
  return null;
11337
11310
  }
11338
- function overFounder(item, stated) {
11339
- if (item.topic !== GOALS_TOPIC || item.provenance === "stated" || !stated.has(item.key)) return null;
11340
- return `the founder set "${item.key}" themselves; drop your proposal, never write it again`;
11311
+ function overFounder(item, stated, definitionLever = null) {
11312
+ if (item.topic !== GOALS_TOPIC || item.provenance === "stated") return null;
11313
+ if (stated.has(item.key)) return `the founder set "${item.key}" themselves; drop your proposal, never write it again`;
11314
+ const lever = item.key === OBJECTIVE_KEY ? LEVER_LINE.exec(item.content)?.[1] : void 0;
11315
+ if (definitionLever && lever && lever !== definitionLever) return `the founder chose ${definitionLever} when they defined their objective: write "${item.key}" for ${definitionLever}, or nothing`;
11316
+ return null;
11341
11317
  }
11342
11318
  function normaliseItem(item) {
11343
11319
  return normalisePersona(item);
@@ -11799,9 +11775,9 @@ ${renderMap()}`,
11799
11775
  valid.push({ ...item, content: fix.content });
11800
11776
  } else valid.push(item);
11801
11777
  }
11802
- const stated = await statedGoals(ctx, valid);
11778
+ const { stated, definitionLever } = await statedGoals(ctx, valid);
11803
11779
  const writable = valid.filter((item) => {
11804
- const why = overFounder(item, stated);
11780
+ const why = overFounder(item, stated, definitionLever);
11805
11781
  if (why) refused.push({ key: item.key, why });
11806
11782
  return !why;
11807
11783
  });
@@ -12367,11 +12343,11 @@ async function listContext(ctx, domain, topic) {
12367
12343
  return { items };
12368
12344
  }
12369
12345
  async function statedGoals(ctx, items) {
12370
- const keys = items.filter((i) => i.topic === GOALS_TOPIC && i.provenance !== "stated").map((i) => i.key);
12371
- if (!keys.length) return /* @__PURE__ */ new Set();
12372
- const { data, error } = await ctx.client.from("memory").select("key").eq("project_id", ctx.projectId).eq("topic", GOALS_TOPIC).in("key", keys).eq("provenance", "stated").is("superseded_by", null);
12346
+ if (!items.some((i) => i.topic === GOALS_TOPIC && i.provenance !== "stated")) return { stated: /* @__PURE__ */ new Set(), definitionLever: null };
12347
+ const { data, error } = await ctx.client.from("memory").select("key, content").eq("project_id", ctx.projectId).eq("topic", GOALS_TOPIC).eq("provenance", "stated").is("superseded_by", null);
12373
12348
  if (error) throw new Error(`write_context failed reading the founder's goals: ${error.message}`);
12374
- return new Set(data.map((r) => r.key));
12349
+ const definition = data.find((r) => r.key === DEFINITION_KEY);
12350
+ return { stated: new Set(data.map((r) => r.key)), definitionLever: definition ? parseDefinition(definition.content)?.lever ?? null : null };
12375
12351
  }
12376
12352
  async function writeContext(ctx, items, refused = []) {
12377
12353
  const written = [];
@@ -13909,7 +13885,7 @@ import { promisify as promisify7 } from "util";
13909
13885
  // package.json
13910
13886
  var package_default = {
13911
13887
  name: "@nanopm/cli",
13912
- version: "0.1.2",
13888
+ version: "0.1.5",
13913
13889
  description: "An autonomous product manager for builders who run several small products. Runs on your machine, drives your own coding harness.",
13914
13890
  type: "module",
13915
13891
  license: "MIT",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nanopm/cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.5",
4
4
  "description": "An autonomous product manager for builders who run several small products. Runs on your machine, drives your own coding harness.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -152,7 +152,9 @@ product is.
152
152
  `nanopm personas` deepens them afterwards with the market view in hand.
153
153
  - one aim (`goals`, key `objective-1`), unless a stated one exists: the founder may
154
154
  have written theirs on the web while you were reading, so list_context with topic
155
- `goals` right before this, and propose nothing over a stated one. If the write is
155
+ `goals` right before this, and propose nothing over a stated one. If the founder
156
+ already defined what it counts (`objective-1-definition`, stated), the aim is on **that
157
+ lever**, never another: they chose it. If the write is
156
158
  refused because `objective-1` already exists, the founder set it meanwhile: drop it,
157
159
  never write it again (dogo-3, 2026-10-01: a retry cost a round trip). Its first words
158
160
  name the lever: `Lever: activation.`, `retention`, `acquisition` or `monetization`;
@@ -171,8 +173,8 @@ product is.
171
173
  code does is the pitch with a wider adjective, and it is exactly the row this rule exists
172
174
  to stop (docs/nanopm-53-direction.md §3). If the product's own words do not say where the
173
175
  company is going, write nothing under these two topics and say so in your final log: the
174
- direction crew writes them later, once the personas and the market are in, which is when
175
- there is something to write them from.
176
+ card asks the founder for the mission in their own words, and the business card drafts a
177
+ mission and a vision with them when they ask. Do not write a gap for it: the card asks.
176
178
  Only when the repo states a stance in words (a README that says "maintenance mode",
177
179
  a roadmap that says "growth"), call set_preferences with `stance_provenance:
178
180
  "inferred"`; otherwise do not call it at all. The stance is `grow` until someone says