loki-mode 9.48.2 → 9.49.2
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/.github/workflows/loki-issue-to-pr.yml +1 -1
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/loki +137 -12
- package/autonomy/provider-offer.sh +47 -0
- package/autonomy/quickstart.sh +306 -9
- package/dashboard/__init__.py +1 -1
- package/dashboard/static/index.html +7 -1
- package/docs/INSTALLATION.md +1 -1
- package/docs/KNOWN-ISSUES.md +42 -0
- package/loki-ts/dist/loki.js +317 -317
- package/mcp/__init__.py +1 -1
- package/package.json +1 -1
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/web-app/dist/assets/{AdminPage-PyyVXg4b.js → AdminPage-DpV2vW3y.js} +1 -1
- package/web-app/dist/assets/{Avatar-CTUlr_Xf.js → Avatar-BXeMYK1_.js} +1 -1
- package/web-app/dist/assets/{Badge-BQeXvmg8.js → Badge-D8WddQk1.js} +1 -1
- package/web-app/dist/assets/{Button-Dc6-P6pz.js → Button-CFzTTVeJ.js} +1 -1
- package/web-app/dist/assets/{CockpitPage-BspOJ3Fv.js → CockpitPage-D1eQe9QF.js} +4 -4
- package/web-app/dist/assets/{ComparePage-CFXIM--Z.js → ComparePage-uX3OPphr.js} +1 -1
- package/web-app/dist/assets/{ErrorBoundary-C7vQYDW9.js → ErrorBoundary-wE3oxaG6.js} +1 -1
- package/web-app/dist/assets/{EvidenceReceiptPanel-CqjsjTOs.js → EvidenceReceiptPanel-B_Wouz6N.js} +1 -1
- package/web-app/dist/assets/{GitHubIssuesPanel-DyuNDMgD.js → GitHubIssuesPanel-DRlEeFuw.js} +1 -1
- package/web-app/dist/assets/{GitHubPRsPanel-BtAq20xF.js → GitHubPRsPanel-CCv6_I74.js} +2 -2
- package/web-app/dist/assets/{HomePage-BuVFOrO8.js → HomePage-EG-jq8nR.js} +1 -1
- package/web-app/dist/assets/{LoginPage-CuhhrxGb.js → LoginPage-CEOnBICH.js} +1 -1
- package/web-app/dist/assets/{MagicPage-D2KZPX3F.js → MagicPage-DeWaFdsG.js} +1 -1
- package/web-app/dist/assets/MetricsPage-CG8CuSSJ.js +6 -0
- package/web-app/dist/assets/{NotFoundPage-CTnnaWlL.js → NotFoundPage-Mz5rOat6.js} +1 -1
- package/web-app/dist/assets/{ProjectPage-uJFaoM8-.js → ProjectPage-D2Fh6tVw.js} +8 -8
- package/web-app/dist/assets/{ProjectsPage-8lKAOBIW.js → ProjectsPage-B8cRWkWa.js} +2 -2
- package/web-app/dist/assets/{SettingsPage-D1BBd5qy.js → SettingsPage-DKpsst6x.js} +1 -1
- package/web-app/dist/assets/{ShowcasePage-CmQ0n8iO.js → ShowcasePage-qab065PM.js} +1 -1
- package/web-app/dist/assets/SystemSettingsPage-9qmEH2Y6.js +1 -0
- package/web-app/dist/assets/{TeamsPage-BS0TtVUa.js → TeamsPage-CW6lvW-0.js} +1 -1
- package/web-app/dist/assets/{TemplatesPage-C2zPnnf2.js → TemplatesPage-B-a61T6a.js} +1 -1
- package/web-app/dist/assets/{TerminalOutput-Dle1klqq.js → TerminalOutput-CJy13rAn.js} +1 -1
- package/web-app/dist/assets/{activity-DQp5U9f9.js → activity-CA4moIvC.js} +1 -1
- package/web-app/dist/assets/{bell-BqkTmgL7.js → bell-JyOeobLN.js} +1 -1
- package/web-app/dist/assets/{bot-DILhaIGH.js → bot-Cu4UwP5w.js} +1 -1
- package/web-app/dist/assets/{check-BC9x7oPm.js → check-Drzd8-9c.js} +1 -1
- package/web-app/dist/assets/{chevron-left--yqvAfjx.js → chevron-left-ClvdXm3Y.js} +1 -1
- package/web-app/dist/assets/{circle-alert-DOH6PcgZ.js → circle-alert-CstyZfKG.js} +1 -1
- package/web-app/dist/assets/{clock-DKSDFsE6.js → clock-Bnz6tlQN.js} +1 -1
- package/web-app/dist/assets/{cloud-DRBU3JYs.js → cloud-IPIgE4P3.js} +1 -1
- package/web-app/dist/assets/{code-xml-D2LqszR9.js → code-xml-BOZbQiOt.js} +1 -1
- package/web-app/dist/assets/{database-D7ceedlh.js → database-CHclPjog.js} +1 -1
- package/web-app/dist/assets/{dollar-sign-D0A47egY.js → dollar-sign-D6RFNgdZ.js} +1 -1
- package/web-app/dist/assets/{file-code-corner-CH_Qbtjo.js → file-code-corner-CNayCvOY.js} +1 -1
- package/web-app/dist/assets/{file-plus-CUNMjHk9.js → file-plus-BmVfFwsX.js} +1 -1
- package/web-app/dist/assets/folder-open-ChzGfLRr.js +11 -0
- package/web-app/dist/assets/{globe-gZnaGg8N.js → globe-DtXgCG3Z.js} +1 -1
- package/web-app/dist/assets/{hammer-BhJC5DMK.js → hammer-DCa0I_7V.js} +1 -1
- package/web-app/dist/assets/{index-BMmYYws0.js → index-Cu-njJ8o.js} +3 -3
- package/web-app/dist/assets/index-D98GzaW8.css +1 -0
- package/web-app/dist/assets/{layers-glXeoUbJ.js → layers-DxIhn7NZ.js} +1 -1
- package/web-app/dist/assets/{lightbulb-gXn8H0Lf.js → lightbulb-D7Pzhd06.js} +1 -1
- package/web-app/dist/assets/{loader-circle-Do2ke8xm.js → loader-circle-DXgc_u5I.js} +1 -1
- package/web-app/dist/assets/{lock-Bgplb1_n.js → lock-qRAVU53b.js} +1 -1
- package/web-app/dist/assets/{mail-j4Lo8hfB.js → mail-CHe3KyCw.js} +1 -1
- package/web-app/dist/assets/{minus-dhrzKSK5.js → minus-B8p0FoYZ.js} +1 -1
- package/web-app/dist/assets/{package-CInWQQ97.js → package-C5LDn8SI.js} +1 -1
- package/web-app/dist/assets/{plus-BpYvoLlZ.js → plus-CRd_FHOA.js} +1 -1
- package/web-app/dist/assets/{refresh-cw-F6koqEgF.js → refresh-cw-DKoSN8s3.js} +1 -1
- package/web-app/dist/assets/{rotate-ccw-CQ2W5S8f.js → rotate-ccw-Nf8zB8G0.js} +1 -1
- package/web-app/dist/assets/{save-DMCYGm06.js → save-B8ytQLgW.js} +1 -1
- package/web-app/dist/assets/{server-Bj8EX4Z1.js → server-D_VRDkaD.js} +1 -1
- package/web-app/dist/assets/{shield-alert-CiYuulCU.js → shield-alert-PsDkSyFD.js} +1 -1
- package/web-app/dist/assets/{thumbs-up-BqEh1puP.js → thumbs-up-Da1QQhqo.js} +11 -6
- package/web-app/dist/assets/{trash-2-CCwouZJJ.js → trash-2-ChoylpGQ.js} +1 -1
- package/web-app/dist/assets/{trending-down-Cx1vhE4o.js → trending-down-CX2fUeho.js} +1 -1
- package/web-app/dist/assets/{trending-up-BUhVYJ2T.js → trending-up-CGyYgZH7.js} +1 -1
- package/web-app/dist/assets/{upload-D5IsZeFM.js → upload-KkeVyg67.js} +1 -1
- package/web-app/dist/assets/{usePolling-CJbfhFcH.js → usePolling-C5f4xrXD.js} +1 -1
- package/web-app/dist/assets/{user-BRLhezsD.js → user-WCGccIST.js} +1 -1
- package/web-app/dist/index.html +2 -2
- package/web-app/server.py +121 -1
- package/web-app/dist/assets/MetricsPage-DFEYJl9z.js +0 -6
- package/web-app/dist/assets/SystemSettingsPage-CSHaVyuD.js +0 -6
- package/web-app/dist/assets/copy-BKW4U4Qr.js +0 -6
- package/web-app/dist/assets/folder-open-CQ_SVq94.js +0 -6
- package/web-app/dist/assets/git-commit-horizontal-DQUBe0oD.js +0 -6
- package/web-app/dist/assets/index-84JUN_aA.css +0 -1
package/dashboard/__init__.py
CHANGED
|
@@ -2444,7 +2444,11 @@ var LokiDashboard=(()=>{var Ie=Object.defineProperty;var zt=Object.getOwnPropert
|
|
|
2444
2444
|
PASSED
|
|
2445
2445
|
</div>
|
|
2446
2446
|
</div>
|
|
2447
|
-
`}
|
|
2447
|
+
`}_renderDisconnected(){return`<div class="journey-state" role="status">
|
|
2448
|
+
Not connected to the Loki API. No session numbers can be shown until it
|
|
2449
|
+
reconnects. Start it with <code>loki dashboard start</code>, or check the
|
|
2450
|
+
API URL in Settings.
|
|
2451
|
+
</div>`}_renderJourney(){if(this._journeyState==="unavailable")return'<div class="journey-state" role="status">Issue-to-PR evidence is unavailable. No readiness claim can be made.</div>';if(this._journeyState!=="ready"||!this._journeyProof)return'<div class="journey-state" role="status">No issue-to-PR receipt yet. Start an issue run to record its first result, proof, and PR state.</div>';let e=this._journeyProof,t=e?.facts?.journey;if(!t||!t.issue)return'<div class="journey-state" role="status">Latest receipt is not an issue run. Issue-to-PR readiness is not applicable.</div>';let i=t.time_to_first_result_sec,a=typeof i=="number"&&i>=0,s=t.first_result_kind==="code_change"?"verified patch":t.first_result_kind==="proposed_solution_plan"?"proposed solution":"result",r=a?`${Math.round(i)}s to ${s}`:"Not measured",o=typeof e?.honesty?.headline=="string"?e.honesty.headline:null,n=Array.isArray(e?.honesty?.degraded)?e.honesty.degraded.length:null,l=o||"Not evaluated",c=n===null?"Uncertainty not measured":n===0?"No recorded gaps":`${n} recorded gap${n===1?"":"s"}`,p=t.pull_request,u="Not prepared",m="";p&&p.state&&(u=String(p.state).replace(/_/g," "),this._isSafeWebUrl(p.url)&&(m=`<a class="journey-link" href="${this._escapeHtml(p.url)}" target="_blank" rel="noopener noreferrer">Open PR</a>`));let g=this._data.phase||(this._data.status==="running"?"In progress":"Run complete");return`
|
|
2448
2452
|
<section class="journey" aria-labelledby="journey-heading">
|
|
2449
2453
|
<div class="journey-heading" id="journey-heading">Issue to PR</div>
|
|
2450
2454
|
<div class="journey-steps">
|
|
@@ -2633,6 +2637,7 @@ var LokiDashboard=(()=>{var Ie=Object.defineProperty;var zt=Object.getOwnPropert
|
|
|
2633
2637
|
<span class="overview-title">Overview</span>
|
|
2634
2638
|
</div>
|
|
2635
2639
|
|
|
2640
|
+
${this._data.connected?`
|
|
2636
2641
|
${this._renderJourney()}
|
|
2637
2642
|
|
|
2638
2643
|
<div class="overview-grid">
|
|
@@ -2687,6 +2692,7 @@ var LokiDashboard=(()=>{var Ie=Object.defineProperty;var zt=Object.getOwnPropert
|
|
|
2687
2692
|
<div class="card-value small-text">${p}</div>
|
|
2688
2693
|
</div>
|
|
2689
2694
|
</div>
|
|
2695
|
+
`:this._renderDisconnected()}
|
|
2690
2696
|
</div>
|
|
2691
2697
|
`}};customElements.get("loki-overview")||customElements.define("loki-overview",X);var Ft=[{id:"pending",label:"Pending",status:"pending",color:"var(--loki-text-muted)"},{id:"in_progress",label:"In Progress",status:"in_progress",color:"var(--loki-blue)"},{id:"review",label:"In Review",status:"review",color:"var(--loki-purple)"},{id:"done",label:"Completed",status:"done",color:"var(--loki-green)"}],Z=class d extends v{static get observedAttributes(){return["api-url","project-id","theme","readonly"]}constructor(){super(),this._tasks=[],this._loading=!0,this._error=null,this._draggedTask=null,this._selectedTask=null,this._expandedCards=new Set,this._selectedTasks=new Set,this._bulkMode=!1,this._activeFilter="all",this._searchQuery="",this._visibleCounts={},this._api=null,this._state=N()}static get PAGE_SIZE(){return 10}_getVisibleCount(e){let t=this._visibleCounts[e];return typeof t=="number"&&t>0?t:d.PAGE_SIZE}_showMore(e){this._visibleCounts[e]=this._getVisibleCount(e)+d.PAGE_SIZE,this.render()}_columnIcon(e){switch(e){case"pending":return'<circle cx="12" cy="12" r="10"/>';case"in_progress":return'<path d="M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83"/>';case"review":return'<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/>';case"done":return'<path d="M22 11.08V12a10 10 0 11-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/>';default:return'<circle cx="12" cy="12" r="10"/>'}}connectedCallback(){super.connectedCallback(),this._setupApi(),this._loadTasks()}disconnectedCallback(){super.disconnectedCallback(),this._teardownApiListeners()}_teardownApiListeners(){this._api&&this._onTaskEvent&&(this._api.removeEventListener(f.TASK_CREATED,this._onTaskEvent),this._api.removeEventListener(f.TASK_UPDATED,this._onTaskEvent),this._api.removeEventListener(f.TASK_DELETED,this._onTaskEvent))}attributeChangedCallback(e,t,i){t!==i&&(e==="api-url"&&this._api&&(this._teardownApiListeners(),this._setupApi(),this._loadTasks()),e==="project-id"&&this._loadTasks(),e==="theme"&&this._applyTheme())}_setupApi(){let e=this.getAttribute("api-url")||window.location.origin;this._api=h({baseUrl:e}),this._onTaskEvent=()=>this._loadTasks(),this._api.addEventListener(f.TASK_CREATED,this._onTaskEvent),this._api.addEventListener(f.TASK_UPDATED,this._onTaskEvent),this._api.addEventListener(f.TASK_DELETED,this._onTaskEvent)}async _loadTasks(){let e=this._api;this._loading=!0,this._error=null,this.render();try{let t=this.getAttribute("project-id"),i=t?{projectId:parseInt(t)}:{},a=await e.listTasks(i);if(e!==this._api)return;this._tasks=a;let s=this._state.get("localTasks")||[];s.length>0&&(this._tasks=[...this._tasks,...s.map(r=>({...r,isLocal:!0}))]),this._state.update({"cache.tasks":this._tasks},!1)}catch(t){if(e!==this._api)return;this._error=t.message,this._tasks=(this._state.get("localTasks")||[]).map(i=>({...i,isLocal:!0}))}this._loading=!1,this.render()}_getTasksByStatus(e){return this._getFilteredTasks().filter(i=>i.status?.toLowerCase().replace(/-/g,"_")===e)}_handleDragStart(e,t){this.hasAttribute("readonly")||(this._draggedTask=t,e.target.classList.add("dragging"),e.dataTransfer.effectAllowed="move",e.dataTransfer.setData("text/plain",t.id.toString()))}_handleDragEnd(e){e.target.classList.remove("dragging"),this._draggedTask=null,this.shadowRoot.querySelectorAll(".kanban-tasks").forEach(t=>{t.classList.remove("drag-over")})}_handleDragOver(e){e.preventDefault(),e.dataTransfer.dropEffect="move"}_handleDragEnter(e){e.preventDefault(),e.currentTarget.classList.add("drag-over")}_handleDragLeave(e){e.currentTarget.contains(e.relatedTarget)||e.currentTarget.classList.remove("drag-over")}async _handleDrop(e,t){if(e.preventDefault(),e.currentTarget.classList.remove("drag-over"),!this._draggedTask||this.hasAttribute("readonly"))return;let i=this._draggedTask.id,a=this._tasks.find(r=>r.id===i);if(!a)return;let s=a.status;if(s!==t){a.status=t,this.render();try{a.isLocal?this._state.moveLocalTask(i,t):await this._api.moveTask(i,t,0),this.dispatchEvent(new CustomEvent("task-moved",{detail:{taskId:i,oldStatus:s,newStatus:t}}))}catch(r){a.status=s,this.render(),console.error("Failed to move task:",r)}}}_toggleCardExpand(e){this._expandedCards.has(e)?this._expandedCards.delete(e):this._expandedCards.add(e),this.render()}_toggleTaskSelection(e,t){t&&t.stopPropagation(),this._selectedTasks.has(e)?this._selectedTasks.delete(e):this._selectedTasks.add(e),this.render()}_toggleBulkMode(){this._bulkMode=!this._bulkMode,this._bulkMode||this._selectedTasks.clear(),this.render()}async _bulkMove(e){let t=[...this._selectedTasks];for(let i of t){let a=this._tasks.find(s=>String(s.id)===String(i));if(a&&a.status!==e)try{a.isLocal?this._state.moveLocalTask(i,e):await this._api.moveTask(i,e,0),a.status=e}catch(s){console.error("Failed to bulk move task:",i,s)}}this._selectedTasks.clear(),this._bulkMode=!1,this.render(),this._loadTasks()}async _bulkDelete(){let e=[...this._selectedTasks];for(let t of e)try{await this._api.deleteTask(t)}catch(i){console.error("Failed to delete task:",t,i)}this._selectedTasks.clear(),this._bulkMode=!1,this._loadTasks()}_setFilter(e){this._activeFilter=e,this._visibleCounts={},this.render()}_setSearch(e){this._searchQuery=e||"",this._visibleCounts={},this._renderTaskRegion();let t=this.shadowRoot.getElementById("task-search");if(t){t.focus();let i=t.value.length;try{t.setSelectionRange(i,i)}catch{}}}_getFilteredTasks(){let e=[...this._tasks],t=new Date,i=new Date(t.getFullYear(),t.getMonth(),t.getDate()),a=new Date(i.getTime()-7*24*60*60*1e3);switch(this._activeFilter){case"today":e=e.filter(r=>{let o=r.created_at?new Date(r.created_at):null;return o&&o>=i});break;case"this-week":e=e.filter(r=>{let o=r.created_at?new Date(r.created_at):null;return o&&o>=a});break;case"running":e=e.filter(r=>r.status==="in_progress");break;case"failed":e=e.filter(r=>r.status==="failed"||r.status==="error");break;default:break}let s=this._searchQuery.trim().toLowerCase();return s&&(e=e.filter(r=>[r.id,r.title,r.description,r.type].map(n=>String(n??"").toLowerCase()).join(" ").includes(s))),e}_openAddTaskModal(e="pending"){this.dispatchEvent(new CustomEvent("add-task",{detail:{status:e}}))}_openTaskDetail(e){this._selectedTask=e,this.render(),this.dispatchEvent(new CustomEvent("task-click",{detail:{task:e}}))}_closeTaskDetail(){this._selectedTask=null,this.render()}_renderMarkdown(e){return C(e)}_formatTimestamp(e){if(!e)return"";try{let t=new Date(e);return isNaN(t.getTime())?this._escapeHtml(String(e)):t.toLocaleString()}catch{return this._escapeHtml(String(e))}}_phaseClass(e){let t=String(e||"").toLowerCase();return["reason","plan","planning"].includes(t)?"phase-reason":["act","execute","execution","implement"].includes(t)?"phase-act":["reflect","review"].includes(t)?"phase-reflect":["verify","test","gate"].includes(t)?"phase-verify":"phase-default"}_logLevelClass(e){let t=String(e||"info").toLowerCase();return t==="error"||t==="fatal"?"log-error":t==="warn"||t==="warning"?"log-warn":t==="debug"||t==="trace"?"log-debug":"log-info"}_renderTaskDetailModal(e){if(!e)return"";let t=(e.priority||"medium").toLowerCase(),i=t.charAt(0).toUpperCase()+t.slice(1),a=e.status||"pending",s=a.replace(/_/g," ").replace(/\b\w/g,g=>g.toUpperCase()),r=e.metadata||{},o=Array.isArray(e.acceptance_criteria)?e.acceptance_criteria:[],n=Array.isArray(e.context_files)?e.context_files:[],l=e.specification||"",c=e.description||"",p=Array.isArray(e.notes)?e.notes:[],u=Array.isArray(e.logs)?e.logs:[],m=e.full_content||"";return`
|
|
2692
2698
|
<div class="modal-overlay" id="task-detail-overlay">
|
package/docs/INSTALLATION.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The flagship product of [Autonomi](https://www.autonomi.dev/). Loki Mode is a spec-driven autonomous builder with a built-in trust layer that takes any spec to a deployed product and verifies completion with evidence (quality gates plus a completion council), not just a "done" claim. Complete installation instructions for all platforms and use cases.
|
|
4
4
|
|
|
5
|
-
**Version:** v9.
|
|
5
|
+
**Version:** v9.49.2
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
|
|
2
|
+
## doctor --json writes 108 bytes to stderr on a provider-less host
|
|
3
|
+
|
|
4
|
+
`bash autonomy/loki doctor --json` on a host with NO provider CLI emits:
|
|
5
|
+
|
|
6
|
+
```
|
|
7
|
+
autonomy/loki: line 14066: required: command not found
|
|
8
|
+
autonomy/loki: line 14066: status: command not found
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
**Not user-affecting, and not a JSON defect.** stdout is 5176 bytes of valid
|
|
12
|
+
JSON; only stderr is polluted. Every `doctor --json` assertion in the repo
|
|
13
|
+
discards stderr (`tests/test-airgap-commands.sh:50`,
|
|
14
|
+
`tests/test-doctor-blocker-parity.sh:129,132`,
|
|
15
|
+
`tests/test-doctor-ci-gateable.sh:46,84,86`).
|
|
16
|
+
`tests/test-e2e-features.sh:104` was the lone outlier using `2>&1` and has been
|
|
17
|
+
brought into line.
|
|
18
|
+
|
|
19
|
+
**Attribution:** introduced this session. `git show 0416810d:autonomy/loki`
|
|
20
|
+
(v9.48.2) emits 0 bytes on the same host; HEAD emits 108. The region was last
|
|
21
|
+
touched by `56e03374 feat(doctor): show provider availability`.
|
|
22
|
+
|
|
23
|
+
**Six hypotheses tested and REFUTED, so the next person does not repeat them:**
|
|
24
|
+
|
|
25
|
+
1. Word-splitting of `$_provider_availability` at `:14066` -- the assignment is
|
|
26
|
+
correctly quoted; reproduced in isolation as OK.
|
|
27
|
+
2. `provider_availability_json` emitting to stderr -- 0 bytes, clean 353-byte
|
|
28
|
+
JSON in isolation.
|
|
29
|
+
3. Its callees `auto_detect_provider` / `check_provider_installed` -- both
|
|
30
|
+
silent.
|
|
31
|
+
4. The JSON value's content -- `required` and `status` appear 0 times in it.
|
|
32
|
+
5. The value being multi-line and breaking the env-assignment chain -- forcing
|
|
33
|
+
`LOKI_PROVIDER_AVAILABILITY=""` as a literal STILL reproduces.
|
|
34
|
+
6. `render_provider_availability` -- silent in isolation, and disabling the call
|
|
35
|
+
still reproduces.
|
|
36
|
+
|
|
37
|
+
**Also known:** `bash -n` parses the file clean; sourcing the file and calling
|
|
38
|
+
`cmd_doctor_json` directly emits 0 bytes, while executing the file emits 108.
|
|
39
|
+
That difference (streaming read vs single parse) is the strongest remaining
|
|
40
|
+
lead. The words `required` and `status` are python identifiers at `:14085-14098`
|
|
41
|
+
inside the `python3 -c "` string that opens at `:14069`.
|
|
42
|
+
|