@brainervirus/workit-pi 1.0.8 → 1.0.9

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/worker.js CHANGED
@@ -7591,7 +7591,7 @@ var workerReportSchema = object({
7591
7591
  }).strict();
7592
7592
  var workerSchema = object({
7593
7593
  assignment: assignmentSchema,
7594
- state: _enum(["assigned", "running", "cancelling", "stopped", "unknown"]),
7594
+ state: _enum(["assigned", "dispatching", "running", "cancelling", "stopped", "unknown"]),
7595
7595
  session: refSchema.nullable(),
7596
7596
  report: workerReportSchema.nullable()
7597
7597
  }).strict();
package/dist/workit.js CHANGED
@@ -7834,7 +7834,7 @@ var workerReportSchema = object({
7834
7834
  }).strict();
7835
7835
  var workerSchema = object({
7836
7836
  assignment: assignmentSchema,
7837
- state: _enum(["assigned", "running", "cancelling", "stopped", "unknown"]),
7837
+ state: _enum(["assigned", "dispatching", "running", "cancelling", "stopped", "unknown"]),
7838
7838
  session: refSchema.nullable(),
7839
7839
  report: workerReportSchema.nullable()
7840
7840
  }).strict();
@@ -10659,6 +10659,95 @@ function evaluateClosure(requestedOutcome, view) {
10659
10659
  });
10660
10660
  }
10661
10661
 
10662
+ // packages/workit-core/src/core/methods.ts
10663
+ var METHODS = {
10664
+ "workit-challenge": { dimensions: ["challenge", "decisions"] },
10665
+ "workit-behavioral-tdd": { dimensions: ["testing"] },
10666
+ "workit-review": { dimensions: ["review"] },
10667
+ "workit-plan": { dimensions: ["artifacts", "continuity"] },
10668
+ "workit-implement": { dimensions: ["delegation"] },
10669
+ "workit-debug": { ruleIds: ["root-cause-investigation"] },
10670
+ "workit-handoff": { ruleIds: ["durable-handoff"] },
10671
+ "workit-deslop": { ruleIds: ["pre-pr-cleanup"] }
10672
+ };
10673
+ var methodMatches = (id, definition, requirement) => definition.dimensions?.includes(requirement.dimension) === true && !(id === "workit-review" && requirement.ruleId === "self-review") || definition.ruleIds?.includes(requirement.ruleId) === true;
10674
+ var assuranceFor = (id, definition, capabilities) => {
10675
+ const keys = new Set([
10676
+ id,
10677
+ ...definition.dimensions ?? [],
10678
+ ...definition.ruleIds ?? []
10679
+ ]);
10680
+ const matches = capabilities.filter((capability) => keys.has(capability.name) || keys.has(capability.surface));
10681
+ if (id === "workit-review" && matches.length === 0)
10682
+ return { assurance: "unavailable", reasons: ["independent review capability is unavailable"] };
10683
+ if (matches.some((capability) => capability.assurance === "unavailable"))
10684
+ return {
10685
+ assurance: "unavailable",
10686
+ reasons: matches.filter((capability) => capability.assurance === "unavailable").map((capability) => capability.reason)
10687
+ };
10688
+ if (matches.some((capability) => capability.assurance === "enforced"))
10689
+ return { assurance: "enforced", reasons: matches.map((capability) => capability.reason) };
10690
+ return { assurance: "agent_guided", reasons: matches.map((capability) => capability.reason) };
10691
+ };
10692
+ function selectMethods(policy, capabilities) {
10693
+ return Object.entries(METHODS).flatMap(([id, definition]) => {
10694
+ const requirements = policy.requirements.filter((requirement) => methodMatches(id, definition, requirement));
10695
+ if (requirements.length === 0)
10696
+ return [];
10697
+ const capability = assuranceFor(id, definition, capabilities);
10698
+ const reasons = [
10699
+ ...new Set(requirements.map((requirement) => requirement.reason)),
10700
+ ...capability.reasons
10701
+ ];
10702
+ if (capability.assurance === "unavailable")
10703
+ reasons.unshift("required capability is unavailable");
10704
+ return [{ id, reason: reasons.join("; "), assurance: capability.assurance }];
10705
+ });
10706
+ }
10707
+ var invariantBootstrap = () => `
10708
+ Workit keeps one accountable lead and one shared task state. Inspect current task
10709
+ state before acting. An empty task.list means no session yet, not permission to
10710
+ skip Workit; for user-requested product, debug, or behavior work with no active
10711
+ or paused task, run task.start then policy.assess before other product mutations.
10712
+ Omitted expectedRevision, expectedWorkspaceRevision, and writer workerId default
10713
+ to the current records; explicit values are still concurrency-checked, so never
10714
+ copy revisions between calls. Happy path: task.list, task.start {intent} with no
10715
+ revisions, policy.assess {assessment: {facts, signals, consequences,
10716
+ verification}} where facts are inferred or observed with file refs,
10717
+ writer.acquire {taskId} before product writes, evidence.record {evidence} where
10718
+ check and review kinds auto-bind the current tree and need no digests. Record
10719
+ self-review requirements with kind review and reviewContext matching your own
10720
+ host session; the lead may also record checks. Fresh-context review requires a
10721
+ session distinct from the task creator and every other evidence recorder. Record
10722
+ verification evidence last: later evidence or commits can reopen fixed findings
10723
+ for re-verification, so verify, resolve, then close with nothing in between.
10724
+ decision.record {binding with taskId/workspaceId from inspect plus presented and
10725
+ approvedContent} only through a native approval question asked receipt-shaped:
10726
+ header Workit decision: <purpose>, the same label repeated in the question
10727
+ text (some host UIs do not render headers), exactly two options approved and
10728
+ rejected; ask once, never re-ask to mint a receipt. task.close {outcome,
10729
+ summary, decisionIds}. A completed native subagent run stops its bound worker
10730
+ by itself; if a worker strands in cancelling with its run verifiably over,
10731
+ repeat worker.cancel {reason} to confirm the stop. Check and review evidence
10732
+ without a bound candidate goes stale; findings close only as fixed with passing
10733
+ verification, dismissed with supporting evidence, or deferred under an approved
10734
+ limitation. Use only the
10735
+ shared operations for task, policy, evidence, finding, decision, worker, writer,
10736
+ and state changes. Authority is bounded by the requested scope, current
10737
+ revision, caller/session provenance, and observed capabilities. Never claim host
10738
+ enforcement or evidence that the host cannot provide. Shell-executed writes are
10739
+ unattested agent-guided work even when a writer is held. Preserve unresolved
10740
+ requirements, gaps, and uncertain workers. Skill routing: slash aliases /wk-*
10741
+ always load on demand. Model-invoked: new instructions, interruptions, or
10742
+ forgotten items mid-task → load workit-steer before responding; about to open
10743
+ a PR or fresh from implementing → load workit-deslop; CI red → load
10744
+ workit-green-run; small-looking change before close or merge → load
10745
+ workit-blast-radius; ambiguous, consequential, or disputed proposal before
10746
+ recommending → load workit-challenge; spec or plan needs a flow diagram →
10747
+ load workit-diagram, a UI sketch → load workit-mockup. Load the skill; never
10748
+ act from memory of it.
10749
+ `.trim();
10750
+
10662
10751
  // packages/workit-core/src/core/task-context.ts
10663
10752
  function reconcileResume(view, observations = []) {
10664
10753
  if (observations.length > 0)
@@ -10668,7 +10757,7 @@ function reconcileResume(view, observations = []) {
10668
10757
  return candidate;
10669
10758
  const staleEvidenceIds = evaluateEvidence(view.task, candidate.data).filter((entry) => entry.status === "stale").map((entry) => entry.evidenceId);
10670
10759
  const blockers = [...view.task.progress.blockers];
10671
- if (view.task.workers.some((entry) => ["running", "cancelling", "unknown"].includes(entry.data.state)))
10760
+ if (view.task.workers.some((entry) => ["dispatching", "running", "cancelling", "unknown"].includes(entry.data.state)))
10672
10761
  blockers.push({
10673
10762
  reason: "worker state requires reconciliation",
10674
10763
  dependentAction: "resume",
@@ -10718,6 +10807,11 @@ function compactTaskContext(view) {
10718
10807
  digest: entry.data.digest,
10719
10808
  references: entry.data.binding.contentRefs.slice(0, 1).map(compactReference)
10720
10809
  }));
10810
+ const methods = view.task.policy ? selectMethods(view.task.policy, view.capabilities).map((method) => ({
10811
+ id: method.id,
10812
+ assurance: method.assurance,
10813
+ reason: compactText(method.reason, COMPACT_TEXT_BYTES) ?? ""
10814
+ })) : [];
10721
10815
  const gaps = Array.from(new Set([
10722
10816
  ...view.evidence.filter((entry) => entry.status === "stale").map((entry) => `stale evidence: ${entry.evidenceId}`),
10723
10817
  ...view.requirements.filter((entry) => entry.status !== "satisfied").map((entry) => entry.reason),
@@ -10727,15 +10821,18 @@ function compactTaskContext(view) {
10727
10821
  objective: compactText(view.task.intent.data.objective, COMPACT_TEXT_BYTES) ?? "",
10728
10822
  status: view.task.status,
10729
10823
  decisions,
10824
+ methods,
10730
10825
  gaps,
10731
10826
  nextAction: compactText(view.task.progress.nextAction, COMPACT_TEXT_BYTES)
10732
10827
  };
10733
10828
  let encoded = JSON.stringify(context);
10734
- while (compactBytes(encoded) > COMPACT_MAX_BYTES && (context.decisions.length || context.gaps.length)) {
10829
+ while (compactBytes(encoded) > COMPACT_MAX_BYTES && (context.decisions.length || context.methods.length || context.gaps.length)) {
10735
10830
  if (context.gaps.length)
10736
10831
  context.gaps.pop();
10737
- else
10832
+ else if (context.decisions.length)
10738
10833
  context.decisions.pop();
10834
+ else
10835
+ context.methods.pop();
10739
10836
  encoded = JSON.stringify(context);
10740
10837
  }
10741
10838
  if (compactBytes(encoded) > COMPACT_MAX_BYTES) {
@@ -10751,94 +10848,47 @@ var exportDigest = (bundle) => sha256({
10751
10848
  sourceWorkspaceId: bundle.sourceWorkspaceId,
10752
10849
  task: bundle.task
10753
10850
  });
10754
- // packages/workit-core/src/core/methods.ts
10755
- var METHODS = {
10756
- "workit-challenge": { dimensions: ["challenge", "decisions"] },
10757
- "workit-behavioral-tdd": { dimensions: ["testing"] },
10758
- "workit-review": { dimensions: ["review"] },
10759
- "workit-plan": { dimensions: ["artifacts", "continuity"] },
10760
- "workit-implement": { dimensions: ["delegation"] },
10761
- "workit-debug": { ruleIds: ["root-cause-investigation"] },
10762
- "workit-handoff": { ruleIds: ["durable-handoff"] },
10763
- "workit-deslop": { ruleIds: ["pre-pr-cleanup"] }
10851
+ // packages/workit-core/src/core/route-intent.ts
10852
+ var GUIDANCE = {
10853
+ "git.branch_setup": "use the Workit git.branch_setup action (workit_external_action in hosts, or `workit action git.branch_setup --payload '{}' --confirm --json` in the CLI)",
10854
+ "hosting.pull_request": "use the Workit hosting.pull_request action (workit_external_action in hosts, or `workit action hosting.pull_request --payload '{...}' --confirm --json` in the CLI), then drive the created PR through workit-babysit"
10764
10855
  };
10765
- var methodMatches = (id, definition, requirement) => definition.dimensions?.includes(requirement.dimension) === true && !(id === "workit-review" && requirement.ruleId === "self-review") || definition.ruleIds?.includes(requirement.ruleId) === true;
10766
- var assuranceFor = (id, definition, capabilities) => {
10767
- const keys = new Set([
10768
- id,
10769
- ...definition.dimensions ?? [],
10770
- ...definition.ruleIds ?? []
10771
- ]);
10772
- const matches = capabilities.filter((capability) => keys.has(capability.name) || keys.has(capability.surface));
10773
- if (id === "workit-review" && matches.length === 0)
10774
- return { assurance: "unavailable", reasons: ["independent review capability is unavailable"] };
10775
- if (matches.some((capability) => capability.assurance === "unavailable"))
10776
- return {
10777
- assurance: "unavailable",
10778
- reasons: matches.filter((capability) => capability.assurance === "unavailable").map((capability) => capability.reason)
10779
- };
10780
- if (matches.some((capability) => capability.assurance === "enforced"))
10781
- return { assurance: "enforced", reasons: matches.map((capability) => capability.reason) };
10782
- return { assurance: "agent_guided", reasons: matches.map((capability) => capability.reason) };
10856
+ var unbalancedQuotes = (segment) => {
10857
+ const text = segment.replace(/\\./gu, "");
10858
+ return (text.match(/"/gu)?.length ?? 0) % 2 !== 0 || (text.match(/'/gu)?.length ?? 0) % 2 !== 0;
10783
10859
  };
10784
- function selectMethods(policy, capabilities) {
10785
- return Object.entries(METHODS).flatMap(([id, definition]) => {
10786
- const requirements = policy.requirements.filter((requirement) => methodMatches(id, definition, requirement));
10787
- if (requirements.length === 0)
10788
- return [];
10789
- const capability = assuranceFor(id, definition, capabilities);
10790
- const reasons = [
10791
- ...new Set(requirements.map((requirement) => requirement.reason)),
10792
- ...capability.reasons
10793
- ];
10794
- if (capability.assurance === "unavailable")
10795
- reasons.unshift("required capability is unavailable");
10796
- return [{ id, reason: reasons.join("; "), assurance: capability.assurance }];
10797
- });
10860
+ var segmentTokens = (segment) => {
10861
+ if (segment.includes("`") || segment.includes("$("))
10862
+ return null;
10863
+ if (unbalancedQuotes(segment))
10864
+ return null;
10865
+ const parts = segment.trim().split(/\s+/u);
10866
+ while (parts.length > 0 && /^[A-Za-z_][A-Za-z0-9_]*=/u.test(parts[0]))
10867
+ parts.shift();
10868
+ return parts.length > 0 ? parts : null;
10869
+ };
10870
+ var unquote = (token) => token.replace(/^['"]|['"]$/gu, "");
10871
+ function shellRouteIntent(command) {
10872
+ if (typeof command !== "string" || command.trim() === "")
10873
+ return null;
10874
+ for (const raw of command.split(/&&|\|\||;|\n/u)) {
10875
+ const parts = segmentTokens(raw);
10876
+ if (!parts)
10877
+ continue;
10878
+ const head = unquote(parts[0]);
10879
+ const second = unquote(parts[1] ?? "");
10880
+ const third = unquote(parts[2] ?? "");
10881
+ if (head === "git" && second === "switch" && (third === "-c" || third === "--create"))
10882
+ return { route: "git.branch_setup", guidance: GUIDANCE["git.branch_setup"] };
10883
+ if (head === "git" && second === "checkout" && (third === "-b" || third === "-B"))
10884
+ return { route: "git.branch_setup", guidance: GUIDANCE["git.branch_setup"] };
10885
+ if (head === "gh" && second === "pr" && third === "create")
10886
+ return { route: "hosting.pull_request", guidance: GUIDANCE["hosting.pull_request"] };
10887
+ if (head === "glab" && second === "mr" && third === "create")
10888
+ return { route: "hosting.pull_request", guidance: GUIDANCE["hosting.pull_request"] };
10889
+ }
10890
+ return null;
10798
10891
  }
10799
- var invariantBootstrap = () => `
10800
- Workit keeps one accountable lead and one shared task state. Inspect current task
10801
- state before acting. An empty task.list means no session yet, not permission to
10802
- skip Workit; for user-requested product, debug, or behavior work with no active
10803
- or paused task, run task.start then policy.assess before other product mutations.
10804
- Omitted expectedRevision, expectedWorkspaceRevision, and writer workerId default
10805
- to the current records; explicit values are still concurrency-checked, so never
10806
- copy revisions between calls. Happy path: task.list, task.start {intent} with no
10807
- revisions, policy.assess {assessment: {facts, signals, consequences,
10808
- verification}} where facts are inferred or observed with file refs,
10809
- writer.acquire {taskId} before product writes, evidence.record {evidence} where
10810
- check and review kinds auto-bind the current tree and need no digests. Record
10811
- self-review requirements with kind review and reviewContext matching your own
10812
- host session; the lead may also record checks. Fresh-context review requires a
10813
- session distinct from the task creator and every other evidence recorder. Record
10814
- verification evidence last: later evidence or commits can reopen fixed findings
10815
- for re-verification, so verify, resolve, then close with nothing in between.
10816
- decision.record {binding with taskId/workspaceId from inspect plus presented and
10817
- approvedContent} only through a native approval question asked receipt-shaped:
10818
- header Workit decision: <purpose>, the same label repeated in the question
10819
- text (some host UIs do not render headers), exactly two options approved and
10820
- rejected; ask once, never re-ask to mint a receipt. task.close {outcome,
10821
- summary, decisionIds}. A completed native subagent run stops its bound worker
10822
- by itself; if a worker strands in cancelling with its run verifiably over,
10823
- repeat worker.cancel {reason} to confirm the stop. Check and review evidence
10824
- without a bound candidate goes stale; findings close only as fixed with passing
10825
- verification, dismissed with supporting evidence, or deferred under an approved
10826
- limitation. Use only the
10827
- shared operations for task, policy, evidence, finding, decision, worker, writer,
10828
- and state changes. Authority is bounded by the requested scope, current
10829
- revision, caller/session provenance, and observed capabilities. Never claim host
10830
- enforcement or evidence that the host cannot provide. Shell-executed writes are
10831
- unattested agent-guided work even when a writer is held. Preserve unresolved
10832
- requirements, gaps, and uncertain workers. Skill routing: slash aliases /wk-*
10833
- always load on demand. Model-invoked: new instructions, interruptions, or
10834
- forgotten items mid-task → load workit-steer before responding; about to open
10835
- a PR or fresh from implementing → load workit-deslop; CI red → load
10836
- workit-green-run; small-looking change before close or merge → load
10837
- workit-blast-radius; ambiguous, consequential, or disputed proposal before
10838
- recommending → load workit-challenge; spec or plan needs a flow diagram →
10839
- load workit-diagram, a UI sketch → load workit-mockup. Load the skill; never
10840
- act from memory of it.
10841
- `.trim();
10842
10892
  // packages/workit-core/src/core/external-action.ts
10843
10893
  var externalActionSchema = discriminatedUnion("operation", [
10844
10894
  object({
@@ -11617,10 +11667,10 @@ var applyWorkerLifecycle = (input) => {
11617
11667
  const notStarted = input.dispatch === true;
11618
11668
  if (notStarted && (input.state !== "stopped" || input.session !== null))
11619
11669
  return failure("invalid_input", "a never-dispatched worker stops with no session");
11620
- if (notStarted && (entry.data.state === "stopped" || entry.data.state === "unknown"))
11621
- return failure("invalid_transition", "worker is no longer awaiting dispatch");
11622
- if (notStarted && (entry.data.session !== null || entry.data.state === "running"))
11670
+ if (notStarted && entry.data.session !== null)
11623
11671
  return failure("invalid_transition", "worker already has an observed session");
11672
+ if (notStarted && !["dispatching", "cancelling"].includes(entry.data.state))
11673
+ return failure("invalid_transition", "worker launch was not claimed");
11624
11674
  if ((input.state === "running" || input.state === "cancelling" || input.state === "stopped" && !notStarted) && !input.session)
11625
11675
  return failure("invalid_input", "running worker observations require a session");
11626
11676
  if (entry.data.session && !sameValue(entry.data.session, input.session))
@@ -12007,7 +12057,7 @@ class WorkitCore {
12007
12057
  activeWorkerBlocker(task, workspace) {
12008
12058
  if (workspace.writer)
12009
12059
  return failure("recovery_required", "writer ownership must be released first");
12010
- if (task.workers.some((entry) => ["running", "cancelling", "unknown"].includes(entry.data.state)))
12060
+ if (task.workers.some((entry) => ["dispatching", "running", "cancelling", "unknown"].includes(entry.data.state)))
12011
12061
  return failure("recovery_required", "worker state requires reconciliation");
12012
12062
  return null;
12013
12063
  }
@@ -12771,7 +12821,12 @@ class WorkitCore {
12771
12821
  return failure("invalid_transition", "worker is not awaiting dispatch");
12772
12822
  return success(mutation.revision, null, {
12773
12823
  ...current,
12774
- workers: current.workers.map((item) => item.id === input.workerId ? { ...item, recordedAt: mutation.now, provenance: attested.data } : item)
12824
+ workers: current.workers.map((item) => item.id === input.workerId ? {
12825
+ ...item,
12826
+ recordedAt: mutation.now,
12827
+ provenance: attested.data,
12828
+ data: { ...item.data, state: "dispatching" }
12829
+ } : item)
12775
12830
  });
12776
12831
  }
12777
12832
  });
@@ -13240,7 +13295,7 @@ class WorkitCore {
13240
13295
  const base = reconcileResume(effectiveView);
13241
13296
  if (!base.ok)
13242
13297
  return base;
13243
- const needsReconciliation = view.task.workers.some((entry) => ["running", "cancelling", "unknown"].includes(observedStates.get(entry.id) ?? entry.data.state));
13298
+ const needsReconciliation = view.task.workers.some((entry) => ["dispatching", "running", "cancelling", "unknown"].includes(observedStates.get(entry.id) ?? entry.data.state));
13244
13299
  const blockers = [...base.data.blockers];
13245
13300
  if (needsReconciliation && !blockers.some((entry) => entry.reason === "worker state requires reconciliation"))
13246
13301
  blockers.push({
@@ -13534,14 +13589,12 @@ var workitContext = (ctx) => {
13534
13589
 
13535
13590
  Native Pi session: ${session}. Project-local Workit state is unavailable until Pi trusts this project.`;
13536
13591
  let taskContext = null;
13537
- let methods = [];
13538
13592
  try {
13539
13593
  const store = new TaskStore(ctx.cwd);
13540
13594
  const tasks = store.listTasks();
13541
13595
  if (tasks.ok) {
13542
13596
  const task = tasks.data.filter((entry) => entry.status !== "closed" && (entry.intent.provenance.session?.kind === "host" && entry.intent.provenance.session.host === "pi" && entry.intent.provenance.session.handle === session || entry.workers.some((worker) => worker.data.session?.kind === "host" && worker.data.session.host === "pi" && worker.data.session.handle === session))).sort((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
13543
13597
  if (task) {
13544
- methods = task.policy ? selectMethods(task.policy, piCapabilities(ctx)) : [];
13545
13598
  const core = new WorkitCore(store, piContext(ctx));
13546
13599
  const view = core.task({
13547
13600
  schemaVersion: 1,
@@ -13554,13 +13607,11 @@ Native Pi session: ${session}. Project-local Workit state is unavailable until P
13554
13607
  }
13555
13608
  }
13556
13609
  } catch {}
13557
- const methodText = methods.length ? `
13558
- Selected methods: ${methods.map((method) => `${method.id} (${method.assurance})`).join(", ")}.` : "";
13559
13610
  const taskText = taskContext ? `
13560
13611
  Current task context: ${taskContext}` : "";
13561
13612
  return `${invariantBootstrap()}
13562
13613
 
13563
- Native Pi session: ${session}.${methodText}${taskText}`;
13614
+ Native Pi session: ${session}.${taskText}`;
13564
13615
  };
13565
13616
 
13566
13617
  // packages/workit-core/src/core/skill-manifests.ts
@@ -17415,6 +17466,16 @@ var registerWorkitTools = (pi, options = {}) => {
17415
17466
  });
17416
17467
  };
17417
17468
  var enforceNativeWriter = (event, ctx) => {
17469
+ if (event.toolName === "bash") {
17470
+ const command = event.input?.command;
17471
+ const route = typeof command === "string" ? shellRouteIntent(command) : null;
17472
+ if (route)
17473
+ return {
17474
+ block: true,
17475
+ reason: `direct branch or PR creation bypasses the Workit route; ${route.guidance}`
17476
+ };
17477
+ return;
17478
+ }
17418
17479
  if (event.toolName !== "write" && event.toolName !== "edit")
17419
17480
  return;
17420
17481
  if (!ctx.isProjectTrusted())
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainervirus/workit-pi",
3
- "version": "1.0.8",
3
+ "version": "1.0.9",
4
4
  "description": "Workit native extension for Pi — tools, method skills, and session continuity",
5
5
  "keywords": [
6
6
  "agentic-coding",
@@ -7,7 +7,9 @@ description: Use after creating a PR (auto-starts, drive default), when a PR nee
7
7
 
8
8
  Babysit starts automatically on PR creation unless declined (`babysit:false`).
9
9
  One babysitter per PR; never mutate PR topology (no rebase strategy changes,
10
- no force-push).
10
+ no force-push). When a PR URL is observed from a route Workit did not enforce
11
+ (for example a raw `gh pr create` the host allowed), load this skill and drive
12
+ that PR anyway; do not claim the Workit route was enforced.
11
13
 
12
14
  ## Before method work
13
15