<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Michael Kaminski</title>
    <description>The latest articles on DEV Community by Michael Kaminski (@makaminski1337).</description>
    <link>https://dev.to/makaminski1337</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1224430%2Fe62f2803-0300-4d09-bf54-cf77c63b5779.png</url>
      <title>DEV Community: Michael Kaminski</title>
      <link>https://dev.to/makaminski1337</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/makaminski1337"/>
    <language>en</language>
    <item>
      <title>Statistical Gating for Agent Instruction Changes</title>
      <dc:creator>Michael Kaminski</dc:creator>
      <pubDate>Sun, 09 Aug 2026 19:16:23 +0000</pubDate>
      <link>https://dev.to/makaminski1337/statistical-gating-for-agent-instruction-changes-2ln8</link>
      <guid>https://dev.to/makaminski1337/statistical-gating-for-agent-instruction-changes-2ln8</guid>
      <description>&lt;p&gt;An edit to an agent's instruction file is a deploy, and I stopped letting mine ship on a hunch.&lt;br&gt;
The daily tuning job that maintains my assistant's &lt;code&gt;CLAUDE.md&lt;/code&gt; now requires a statistically&lt;br&gt;
significant improvement — Welch's t-test, p &amp;lt; 0.10, at least a 5% lift — measured against a&lt;br&gt;
14-day rolling baseline, before an instruction change is allowed to stay.&lt;/p&gt;

&lt;p&gt;Then I ran the power calculation on my own gate and found the 5% threshold is decorative. At&lt;br&gt;
14 days per window the test can only detect a shift of roughly &lt;strong&gt;0.97 standard deviations&lt;/strong&gt;.&lt;br&gt;
Anything smaller is invisible regardless of what the lift threshold claims.&lt;/p&gt;

&lt;p&gt;Both halves of that are this post. The mechanism is worth copying. The window size is the part&lt;br&gt;
I got wrong, and the arithmetic is short enough to check.&lt;/p&gt;

&lt;h2&gt;
  
  
  The prompt is the least-tested code in most agent systems
&lt;/h2&gt;

&lt;p&gt;Every other artifact in an agent pipeline has a gate. Application code gets a test suite and a&lt;br&gt;
review. Infrastructure gets a plan and a diff — I have written enough Terraform modules and&lt;br&gt;
managed enough remote state to know nobody merges those blind.&lt;/p&gt;

&lt;p&gt;The instruction file gets none of that. Someone notices the agent did something annoying, adds&lt;br&gt;
a line telling it not to, and ships. There is no baseline, no holdout, and no record of whether&lt;br&gt;
the previous eleven lines are still earning their tokens.&lt;/p&gt;

&lt;p&gt;That is how instruction files rot. They accumulate rules that were true about one bad afternoon&lt;br&gt;
and have been costing context ever since.&lt;/p&gt;

&lt;h2&gt;
  
  
  You cannot test what you do not score
&lt;/h2&gt;

&lt;p&gt;The gate needs a dependent variable, so the first build was a rubric, not a test. Five&lt;br&gt;
dimensions, each scored 0–10 per conversation: goal clarity, rework rate, context hit rate,&lt;br&gt;
scope discipline, and response density. The daily score is the average across that day's&lt;br&gt;
conversations, weighted by conversation length.&lt;/p&gt;

&lt;p&gt;The rubric is frozen. Changing it invalidates every historical comparison, so a rubric change is&lt;br&gt;
itself a tracked meta-edit that resets all baselines.&lt;/p&gt;

&lt;p&gt;Scores are anchored to observable friction rather than self-assessment. The scorer parses&lt;br&gt;
transcripts for five pattern classes — corrections ("no, that", "actually", "don't"), rework&lt;br&gt;
("redo", "start over"), scope drift ("I didn't ask", "just do"), missed context ("I told you&lt;br&gt;
before", "check memory"), and praise ("exactly", "nailed it"). Friction pulls the dimension&lt;br&gt;
score down, praise pulls it up.&lt;/p&gt;

&lt;p&gt;This is the load-bearing decision. An agent grading its own transcripts will drift toward&lt;br&gt;
generosity. Regex over the human's actual words will not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Welch, not Student, and why that is not pedantry
&lt;/h2&gt;

&lt;p&gt;Each edit gets a 14-day pre-window and a 14-day post-window of daily scores, compared with&lt;br&gt;
Welch's t-test. Three outcomes: p &amp;lt; 0.10 and lift ≥ 5% marks the edit &lt;strong&gt;KEPT&lt;/strong&gt;; p &amp;lt; 0.10 and&lt;br&gt;
lift ≤ −5% triggers an &lt;strong&gt;automatic revert&lt;/strong&gt;; everything else is &lt;strong&gt;INCONCLUSIVE&lt;/strong&gt; and the edit&lt;br&gt;
stays on probation.&lt;/p&gt;

&lt;p&gt;Welch rather than Student's t because the two windows should not have equal variance. A good&lt;br&gt;
instruction usually works by removing a failure mode, which compresses the bad tail — the&lt;br&gt;
variance drops as much as the mean rises.&lt;/p&gt;

&lt;p&gt;Student's t assumes equal variance and over-rejects when the smaller-variance group is the&lt;br&gt;
larger sample. That is precisely the case you care about, so the pooled test would hand you your&lt;br&gt;
most confident false positives on your best edits. Welch costs a few degrees of freedom and&lt;br&gt;
removes the assumption.&lt;/p&gt;

&lt;h2&gt;
  
  
  The arithmetic that broke the design
&lt;/h2&gt;

&lt;p&gt;A two-sample test at α = 0.10 and 80% power detects a minimum effect of roughly&lt;br&gt;
(t_α/2 + t_β) × √(2/n) standard deviations. For a 14-day window that is:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Days per window&lt;/th&gt;
&lt;th&gt;Minimum detectable effect&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;1.42 SD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;0.97 SD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;21&lt;/td&gt;
&lt;td&gt;0.78 SD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;28&lt;/td&gt;
&lt;td&gt;0.67 SD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;42&lt;/td&gt;
&lt;td&gt;0.55 SD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;60&lt;/td&gt;
&lt;td&gt;0.46 SD&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftmlaotx498raiq3kh42h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftmlaotx498raiq3kh42h.png" alt="Minimum detectable effect by window length" width="800" height="452"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The gate can only see effects above the yellow line. The 5% threshold sits inside the shaded band — under it the whole time, doing no work.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now convert to the units the gate actually uses. Assume a 7.0 baseline on the 0–10 rubric and a&lt;br&gt;
day-to-day standard deviation of 0.6 points, which is unremarkable for a metric averaged over a&lt;br&gt;
handful of conversations.&lt;/p&gt;

&lt;p&gt;A 5% lift is 0.35 points, or 0.58 SD. The 14-day window detects 0.97 SD, which is 0.58 points —&lt;br&gt;
&lt;strong&gt;8.3% of baseline&lt;/strong&gt;. The statistical test is roughly 1.7× stricter than the lift threshold&lt;br&gt;
sitting next to it.&lt;/p&gt;

&lt;p&gt;So the 5% number never binds. It is doing no work. Every edit that clears the t-test has already&lt;br&gt;
cleared 5% by a wide margin, and every edit that fails does so on power, not on effect size.&lt;/p&gt;

&lt;p&gt;Detecting a genuine 5% lift at that variance takes &lt;strong&gt;38 days per window&lt;/strong&gt;, not 14. And the&lt;br&gt;
requirement moves fast with variance:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Daily score SD&lt;/th&gt;
&lt;th&gt;5% lift, in SD&lt;/th&gt;
&lt;th&gt;Days/window needed&lt;/th&gt;
&lt;th&gt;What 14 days actually detects&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0.4&lt;/td&gt;
&lt;td&gt;0.88&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;5.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0.5&lt;/td&gt;
&lt;td&gt;0.70&lt;/td&gt;
&lt;td&gt;27&lt;/td&gt;
&lt;td&gt;6.9%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0.6&lt;/td&gt;
&lt;td&gt;0.58&lt;/td&gt;
&lt;td&gt;38&lt;/td&gt;
&lt;td&gt;8.3%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0.8&lt;/td&gt;
&lt;td&gt;0.44&lt;/td&gt;
&lt;td&gt;66&lt;/td&gt;
&lt;td&gt;11.1%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1.0&lt;/td&gt;
&lt;td&gt;0.35&lt;/td&gt;
&lt;td&gt;102&lt;/td&gt;
&lt;td&gt;13.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0kwsdh1dxek33aacnf7i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0kwsdh1dxek33aacnf7i.png" alt="What a 5% threshold actually means, by variance" width="800" height="393"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The honest label on the gate. Every bar right of the dashed line is a threshold the config never mentions.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Read the last column as the honest label on the gate. At SD 1.0 a "5% threshold" is really a 14%&lt;br&gt;
threshold, and the difference is entirely hidden from whoever reads the config file.&lt;/p&gt;

&lt;p&gt;The window has to be sized to the variance, not to a calendar. Fourteen days was chosen because&lt;br&gt;
two weeks is a tidy number, which is not a reason.&lt;/p&gt;

&lt;p&gt;There are two ways out and only one of them is cheap. Lengthen the window, and you wait longer&lt;br&gt;
per lesson. Or shrink the variance by scoring more conversations per day — the daily mean's&lt;br&gt;
standard error falls with √n, so tripling daily volume cuts the SD by about 42% and pulls the&lt;br&gt;
required window from 38 days down to roughly 17.&lt;/p&gt;

&lt;p&gt;Put the same arithmetic the other way around. If an edit truly delivers a 5% lift, a 14-day&lt;br&gt;
window calls it significant &lt;strong&gt;44% of the time&lt;/strong&gt;. That is a coin flip on your own best changes.&lt;br&gt;
At 38 days it is 81%.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7f7ln1g6bu80pbx62bcd.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7f7ln1g6bu80pbx62bcd.gif" alt="Animated: power to detect a real 5% lift, by window length" width="800" height="452"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Same arithmetic, run forward. Each frame is a window length; the shaded tail is how often a&lt;br&gt;
genuinely good edit gets called significant.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What the gate cannot do, stated plainly
&lt;/h2&gt;

&lt;p&gt;It does not control for multiple comparisons. At the configured cadence of one low-risk edit&lt;br&gt;
every three days, that is about 122 evaluations a year. At α = 0.10 two-sided, noise alone&lt;br&gt;
produces roughly &lt;strong&gt;six spurious KEPT verdicts and six spurious auto-reverts per year&lt;/strong&gt;. The&lt;br&gt;
auto-revert side is the one that stings: the system will occasionally roll back a good edit with&lt;br&gt;
statistical confidence.&lt;/p&gt;

&lt;p&gt;It is also not a randomized experiment. Pre and post windows are consecutive calendar time, so a&lt;br&gt;
model version change, a vacation, or a month of unusually messy work lands entirely in one window&lt;br&gt;
and gets attributed to the edit.&lt;/p&gt;

&lt;p&gt;That makes this a noise filter, not a causal claim. It stops the obviously-worse edits and the&lt;br&gt;
obviously-imaginary wins. It will not tell you why anything moved.&lt;/p&gt;

&lt;h2&gt;
  
  
  The safeguards do more work than the test
&lt;/h2&gt;

&lt;p&gt;Three rules keep the loop from eating itself, and they matter more than the p-value.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cooldowns.&lt;/strong&gt; One low-risk auto-edit per three days, one high-risk proposal per week. Without a&lt;br&gt;
cooldown the windows overlap so badly that no edit is ever cleanly attributable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Plateau detection.&lt;/strong&gt; If the 14-day rolling score has not improved 2% over the prior 14 days and&lt;br&gt;
no friction is firing, the day's edit is skipped. The easy wins arrive early; after that, editing&lt;br&gt;
is mostly a way to add variance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A risk split with a human in it.&lt;/strong&gt; Wording and formatting changes auto-apply. Anything that&lt;br&gt;
adds a section or changes how the agent decides gets written to a proposals folder and waits.&lt;br&gt;
Every edit backs up the prior file first and lands in a revertible log, tagged, so a bad call is&lt;br&gt;
a one-line rollback rather than an archaeology project.&lt;/p&gt;

&lt;p&gt;The config file has a section titled "when NOT to edit," and the last line of it is the most&lt;br&gt;
useful thing in the whole system: &lt;em&gt;doing nothing is always a valid action.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The cost
&lt;/h2&gt;

&lt;p&gt;Latency. That is the whole bill, and it is larger than I estimated when I built this.&lt;/p&gt;

&lt;p&gt;A gated instruction file learns on a 14-day clock at best and, if the variance numbers above are&lt;br&gt;
right, a 38-day clock in practice. An ungated one learns in an afternoon and is wrong in ways&lt;br&gt;
nobody measures. I would still take the slow version, but I would not pretend the tradeoff is&lt;br&gt;
free, and I would not build this at all for a system I was still prototyping.&lt;/p&gt;

&lt;p&gt;I have no KEPT verdicts to report yet, because the first honest one cannot exist until a full&lt;br&gt;
post-window closes. Publishing the design before the results is the point — the design is the&lt;br&gt;
part that is checkable, and the arithmetic above is the part I would want someone to argue with.&lt;/p&gt;

&lt;p&gt;If you are gating prompt or instruction changes statistically at your shop, I want to know what&lt;br&gt;
window you landed on and what your daily variance looks like. That number is the whole ballgame&lt;br&gt;
and almost nobody publishes it.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://www.michael-kaminski.io/writing/statistical-gating-for-agent-instruction-changes" rel="noopener noreferrer"&gt;michael-kaminski.io&lt;/a&gt;. I write field notes on agent infrastructure — evals, MCP servers, and what it costs to run agents in production.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>statistics</category>
      <category>devops</category>
    </item>
    <item>
      <title>🚀 Boosting Our Project with Strategic Unit Testing: A Closer Look at Our Latest Code 🛠️</title>
      <dc:creator>Michael Kaminski</dc:creator>
      <pubDate>Sun, 14 Jan 2024 19:20:58 +0000</pubDate>
      <link>https://dev.to/makaminski1337/boosting-our-project-with-strategic-unit-testing-a-closer-look-at-our-latest-code-5c66</link>
      <guid>https://dev.to/makaminski1337/boosting-our-project-with-strategic-unit-testing-a-closer-look-at-our-latest-code-5c66</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Hello Devs! 👋 We've just rolled out an exciting update in our project, integrating a series of unit tests that are set to elevate our application's reliability and maintainability to new heights. Let’s dive deep into this new code block and explore its impact on our SDLC. Check out our &lt;a href="https://github.com/MAKaminski/IconGenerator"&gt;repo&lt;/a&gt; here for the full codebase!&lt;/p&gt;

&lt;p&gt;The New Code Block&lt;br&gt;
Here's a snippet from our latest addition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import unittest
from unittest.mock import patch, MagicMock, Mock
from PIL import Image
from io import BytesIO
import main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This segment sets the stage for our unit testing, importing essential modules and our main module where our business logic resides.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tests 🔍
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Testing User Input Prompt
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@patch('main.input', return_value='test')
def test_prompt_user_for_theme(self, input):
    self.assertEqual(main.prompt_user_for_theme(), 'test')

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;👉 This test ensures that our prompt_user_for_theme function correctly captures and returns user input.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Fetching Images from Unsplash
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def test_fetch_images_from_unsplash(self):
    # Mock setup...
    with patch('main.requests.get', return_value=mock_response) as mock_get:
        images = main.fetch_images_from_unsplash('test', {'match_aspect_ratio': False})
        self.assertEqual(len(images), 1)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;👉 By mocking a response, this test verifies if the correct number of images is fetched from Unsplash.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Image Conversion to Grayscale
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def test_convert_to_grayscale_and_contrast(self):
    img = Image.new('RGB', (64, 64))
    converted_img = main.convert_to_grayscale_and_contrast(img)
    self.assertEqual(converted_img.mode, 'L')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;👉 This snippet checks if our image conversion functionality is up to the mark.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Saving Icons to Directory
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@patch('main.os.path.exists', return_value=False)
@patch('main.os.makedirs')
@patch('PIL.Image.new')
def test_save_icons_to_directory(self, mock_new, mock_makedirs, mock_exists):
    # Test implementation...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;👉 It tests the creation of directories and saving of icons, ensuring our file system interactions are flawless.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Value of These Tests 💡
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Reliability Boost:&lt;/strong&gt; Ensures each component works correctly in isolation.&lt;br&gt;
&lt;strong&gt;Speedy Debugging:&lt;/strong&gt; Pinpoints issues, cutting down on troubleshooting time.&lt;br&gt;
&lt;strong&gt;Encourages Better Design:&lt;/strong&gt; Leads to a more modular and maintainable codebase.&lt;br&gt;
&lt;strong&gt;CI Integration:&lt;/strong&gt; Ready for automated testing in CI pipelines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Impact on SDLC 🔄
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Planning &amp;amp; Analysis&lt;/strong&gt;&lt;br&gt;
1.: Our tests provide clarity on functionality, aiding in precise planning.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Design:&lt;/strong&gt; They encourage a design that's modular and easy to test.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Implementation:&lt;/strong&gt; Immediate feedback on code changes reduces bugs.&lt;br&gt;
&lt;strong&gt;Testing &amp;amp; Integration:&lt;/strong&gt; Continuous testing elevates quality and integration.&lt;br&gt;
&lt;strong&gt;Maintenance:&lt;/strong&gt; They make updates and extensions safer and easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;🌟 The addition of these unit tests marks a significant leap in our quest for a robust and maintainable application. By ensuring each component's functionality, we not only prevent bugs but foster a development culture centered around quality and efficiency.&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;Explore More:&lt;/strong&gt; Dive into our &lt;a href="https://github.com/MAKaminski/IconGenerator"&gt;repository&lt;/a&gt; here for the full code and more insights!&lt;/p&gt;

&lt;p&gt;📢 Stay tuned for more updates and let's code towards excellence together! 🚀💻&lt;/p&gt;

</description>
      <category>python</category>
      <category>unittest</category>
      <category>sdlc</category>
    </item>
    <item>
      <title>Introducing Unsplash Image Fetcher: A Practical Approach to Image Processing</title>
      <dc:creator>Michael Kaminski</dc:creator>
      <pubDate>Sun, 14 Jan 2024 18:48:28 +0000</pubDate>
      <link>https://dev.to/makaminski1337/introducing-unsplash-image-fetcher-a-practical-approach-to-image-processing-29eh</link>
      <guid>https://dev.to/makaminski1337/introducing-unsplash-image-fetcher-a-practical-approach-to-image-processing-29eh</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;Welcome to the first iteration of the Unsplash Image Fetcher, a Python script tailored for developers, designers, and content creators. This script efficiently sources and processes images based on your input theme, simplifying a task that often consumes valuable time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Version 1.0 Features
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Theme-Based Image Fetching:&lt;/strong&gt; Select a theme and the script fetches corresponding images from Unsplash.&lt;br&gt;
&lt;strong&gt;Resizing:&lt;/strong&gt; Automatically resizes images to 64x64 pixels.&lt;br&gt;
Aspect Ratio Selection: Option to fetch images with equal aspect ratios.&lt;br&gt;
&lt;strong&gt;Grayscale and Contrast Enhancement:&lt;/strong&gt; Converts images into high-contrast grayscale versions.&lt;br&gt;
&lt;strong&gt;Organized Saving:&lt;/strong&gt; Saves original and transformed images in separate directories for easy access.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code Snippet: Fetching and Resizing Images
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def fetch_images_from_unsplash(theme, config):
    """
    Fetches images from Unsplash based on the provided theme and configuration.
    If 'match_aspect_ratio' in the configuration is True, only fetches images that have an equal aspect ratio.
    Resizes the fetched images to 64x64 pixels and returns them as a list of PIL Image objects.
    """
    print(f"Fetching images for theme: {theme}")
    response = requests.get(f'https://api.unsplash.com/search/photos?query={theme}&amp;amp;client_id={UNSPLASH_ACCESS_KEY}')

    if response.status_code == 200:
        data = response.json()
        images = []
        for i, result in enumerate(data['results']):
            img_url = result['urls']['small']
            img_response = requests.get(img_url)
            img = Image.open(BytesIO(img_response.content))

            # Check if the image has an equal aspect ratio
            if not config['match_aspect_ratio'] or img.width == img.height:
                # Resize the image to 64x64 pixels
                img = img.resize((64, 64))
                images.append(img)
                print(f"Image {i+1} fetched and resized")
            else:
                print(f"Image {i+1} skipped due to unequal aspect ratio")

        print(f"Finished fetching images for theme: {theme}")
        print(f"Number of images retrieved: {len(images)}")
        if len(images) == 0:
            print(colored('No images were retrieved.', 'red'))
        print('-'*50)
        return images
    else:
        print(f"Failed to get images for theme '{theme}' from Unsplash API. Status code: {response.status_code}")
        return []
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Current Limitations and Upcoming Version 2.0
&lt;/h2&gt;

&lt;p&gt;While effective, version 1.0 has its limits. It's a standalone script without a user interface, making it more suitable for those comfortable with command-line tools. Recognizing this, version 2.0 aims to include:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;React App Integration:&lt;/strong&gt; Launching a user-friendly front-end.&lt;br&gt;
&lt;strong&gt;Enhanced Customization:&lt;/strong&gt; More configuration options for image processing.&lt;br&gt;
&lt;strong&gt;User Feedback Integration:&lt;/strong&gt; Implementing features based on user suggestions.&lt;/p&gt;

&lt;h1&gt;
  
  
  Ideation for Version 2.0 Features
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Batch Processing:&lt;/strong&gt; Allow multiple themes in one go for bulk image handling.&lt;br&gt;
&lt;strong&gt;Image Filtering Options:&lt;/strong&gt; Introduce filters like brightness, contrast, and saturation adjustments.&lt;br&gt;
&lt;strong&gt;Download Quality Selection:&lt;/strong&gt; Choose the resolution for downloaded images.&lt;br&gt;
&lt;strong&gt;Interactive UI:&lt;/strong&gt; A simple and intuitive interface for non-programmers.&lt;br&gt;
&lt;strong&gt;Social Media Integration:&lt;/strong&gt; Directly upload edited images to social platforms.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code Example: Grayscale Conversion
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def convert_to_grayscale_and_contrast(image):
    """
    Converts the provided image to grayscale and applies high contrast.
    Returns the converted image.
    """
    grayscale_image = ImageOps.grayscale(image)
    high_contrast_image = ImageOps.autocontrast(grayscale_image)
    return high_contrast_image
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Repository and Collaboration
&lt;/h2&gt;

&lt;p&gt;The script is available on GitHub, inviting collaboration and improvements. You're encouraged to contribute ideas, report bugs, or suggest enhancements. Check out the &lt;a href="https://github.com/MAKaminski/IconGenerator"&gt;repo&lt;/a&gt; here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Version 1.0 of the Unsplash Image Fetcher marks the beginning of a journey towards more streamlined image processing. It's a functional tool, but with your feedback and contributions, version 2.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Embark on a Journey Towards Your Goals with VisionQuestFrameworks Pt 1 of X</title>
      <dc:creator>Michael Kaminski</dc:creator>
      <pubDate>Mon, 18 Dec 2023 06:08:03 +0000</pubDate>
      <link>https://dev.to/makaminski1337/embark-on-a-journey-towards-your-goals-with-visionquestframeworks-pt-1-of-x-337l</link>
      <guid>https://dev.to/makaminski1337/embark-on-a-journey-towards-your-goals-with-visionquestframeworks-pt-1-of-x-337l</guid>
      <description>&lt;p&gt;TLDR;&lt;br&gt;
Repo Quick-Access&lt;br&gt;
Primary App: &lt;a href="https://github.com/MAKaminski/VisionQuest"&gt;VisionQuest&lt;/a&gt;&lt;br&gt;
Side-Project: &lt;a href="https://github.com/MAKaminski/data_analysis"&gt;data_analysis&lt;/a&gt;&lt;br&gt;
Extension: &lt;a href="https://marketplace.visualstudio.com/items?itemName=kaih2o.python-resource-monitor"&gt;Python Resource Monitor&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;Hey there, tech enthusiasts and dream chasers! Welcome to the inaugural chapter of our weekly series on VisionQuest, a project that's about to transform the way you chase your dreams and goals. In this journey, we'll dive into the exciting world of VisionQuest, from its grand vision to the progress we've made so far.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IyUzepVB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x9ay5uwhy2t6eq3whgf2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IyUzepVB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x9ay5uwhy2t6eq3whgf2.png" alt="Grand Vision" width="259" height="194"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Grand Vision:&lt;/strong&gt; Your Personal AI Coach 🤖&lt;br&gt;
At the core of VisionQuest lies a bold aspiration: to create a Personal AI Coach, omni-scient, and ever present - basically clippy back from windows 95.  Ok, but we make some enhancements – an AI mentor that not only comprehends your goals but also tracks your journey and offers timely guidance to ensure your success. Whether you're striving for personal growth, professional achievements, or a healthier lifestyle, VisionQuest is your trusted companion on this path to greatness.&lt;/p&gt;

&lt;p&gt;In my case the goal of VisionQuest is to keep me guided towards my goals, with auto-coaching.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cXQAZtOY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1ht7gfwaer749h2pofl5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cXQAZtOY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1ht7gfwaer749h2pofl5.png" alt="Auto-Coaching" width="800" height="424"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Progress Report 🚀
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Ideation Phase&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here's what we're initially setting out to accomplish:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Goal Setting:&lt;/strong&gt; Our journey began with the simple act of setting and tracking goals. VisionQuest empowers you to remain laser-focused on what matters most to you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Customizable Coaching Intervals:&lt;/strong&gt; We introduced coaching intervals of 2 minutes, 5 minutes, 10 minutes, or 20 minutes, letting you tailor the guidance to your pace.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI-Powered Coaching:&lt;/strong&gt; Our AI algorithms are in a constant state of evolution, delivering personalized coaching that aligns with your goals and progress.&lt;/p&gt;

&lt;p&gt;The actual progress we make week-by-week will vary depending on anything we get hung-up on.  This is an educational journey, or perhaps a case-study in how VisionQuest may be able to keep a wandering mind on track.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0PwvuCwk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iaebdiae9wta5tak45h2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0PwvuCwk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iaebdiae9wta5tak45h2.png" alt="Stay Focused" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Diving into the VisionQuest Codebase
&lt;/h2&gt;

&lt;p&gt;The codebase of VisionQuest, a PyQt5-based application that interacts with the OpenAI API to generate responses to user prompts. You can check out the full code on &lt;a href="https://github.com/MAKaminski/VisionQuest/tree/main"&gt;Github&lt;/a&gt;, but I'll be sharing some juicy snippets right here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Heart of the App: MainWindow&lt;/strong&gt;&lt;br&gt;
At the heart of the application is the MainWindow class. This is where the magic happens. It's responsible for the user interface and the main functionality of the application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("VisionQuest")
        self.userName = self.getUserName()
        self.initUI()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Setting the Stage: initUI&lt;/strong&gt;&lt;br&gt;
The initUI method is where we set up the stage for our application. We're talking window flags, background, position, and transparency. We also create the chat log and send prompt sections here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def initUI(self):
    self.setWindowFlags(Qt.FramelessWindowHint)
    self.setWindowOnTop()
    self.setGradientBackground()
    self.moveWindowToTopRight()
    self.setTransparency()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Pulse: Frequency and Countdown&lt;/strong&gt;&lt;br&gt;
The createMenuBar method is where we create a menu bar with frequency options. This frequency determines how often a prompt is sent to the OpenAI API. We also have a countdown timer that ticks down to the next prompt.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def createMenuBar(self):
    menu_bar = self.menuBar()
    self.createFrequencyMenu(menu_bar)
    self.frequency = 5  # Default frequency is 5 seconds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Conversation Starter: sendPrompt&lt;/strong&gt;&lt;br&gt;
The sendPrompt method is where we send prompts to the OpenAI API and receive responses. We select a random prompt from a list, send it to the API, log the response, and update the chat log.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def sendPrompt(self):
    prompt = random.choice(prompts)
    response = oapi.get_openai_response(prompt)
    self.log_to_db("OpenAI_Model", response, "receiver")
    self.updateChatLog()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Memory: Chat Log&lt;/strong&gt;&lt;br&gt;
The createChatLog and updateChatLog methods are where we create and update the chat log. The chat log is stored in a SQLite database, because who doesn't love a good SQLite database?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def createChatLog(self):
    conn = sqlite3.connect('chat_log.db')
    c = conn.cursor()
    c.execute('''CREATE TABLE IF NOT EXISTS chat_log
                (id INTEGER PRIMARY KEY AUTOINCREMENT, time TEXT, type TEXT, username TEXT, message TEXT)''')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Interacting with OpenAI's GPT-3.5 Turbo Model
&lt;/h2&gt;

&lt;p&gt;In this section, we will look at a Python script that uses OpenAI's GPT-3.5 Turbo model to generate responses to user questions. This can be useful in a variety of applications, such as chatbots, virtual assistants, and more.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code Explanation&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from openai import OpenAI

vision_quest_api = 'OpenAI-Key'
client = OpenAI(api_key=vision_quest_api)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We start by importing the OpenAI module and initializing a client with our OpenAI API key.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def get_openai_response(user_question):
    completion = client.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=[
            {"role": "system", "content": "You are a productivity enhancer, evaluating what people are doing at fixed intervals and recommending specific paths of action to help them reach their ultimate goal."},
            {"role": "user", "content": user_question}
        ]
    )

    return completion.choices[0].message.content
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The get_openai_response function takes a user question as an argument. It sends a chat completion request to the GPT-3.5 Turbo model with two messages: a system message that sets the behavior of the assistant, and a user message that contains the user's question.&lt;/p&gt;

&lt;p&gt;The system message tells the model that it is a "productivity enhancer" that recommends actions to help people reach their goals. This sets the context for the conversation and guides the model's responses.&lt;/p&gt;

&lt;p&gt;The user message is the question that we want the model to answer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ed4UUYcC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yec0f60tn9qcwi9ia62y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ed4UUYcC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yec0f60tn9qcwi9ia62y.png" alt="Messaging" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The function then returns the content of the model's response. This is the text that the model generated in response to the user's question.&lt;/p&gt;

&lt;h2&gt;
  
  
  Convert Images to Icons with PyQt5 and PIL
&lt;/h2&gt;

&lt;p&gt;We wanted to ensure we had a neat looking logo that matched our theme, we prompted DALE for an image - which was saved as .jpeg, then required conversion to .ico - we decided to create a utility. &lt;/p&gt;

&lt;p&gt;In this section, we will explore a simple Python script that uses PyQt5 and PIL (Python Imaging Library) to convert images to icons. This can be useful in various scenarios, such as when you're developing a desktop application and need to create an icon for it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code Explanation&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from PyQt5.QtWidgets import QApplication, QFileDialog
from PIL import Image
import sys
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We start by importing the necessary modules. QApplication and QFileDialog from PyQt5 are used to create a simple GUI for selecting the image file. Image from PIL is used to handle the image processing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def convert_to_icon(filename):
    img = Image.open(filename)
    img = img.resize((32, 32))  # resize image to 32x32 pixels
    ico_filename = filename.rsplit('.', 1)[0] + '.ico'
    img.save(ico_filename, format='ICO', sizes=[(32,32)])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The convert_to_icon function takes a filename as an argument. It opens the image, resizes it to 32x32 pixels (the standard size for icons), and saves it in ICO format. The new icon file will have the same name as the original image file, but with the .ico extension.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if __name__ == "__main__":
    app = QApplication([])
    filename, _ = QFileDialog.getOpenFileName()
    if filename:
        convert_to_icon(filename)
    sys.exit(app.exec_())
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the main part of the script, we create a QApplication instance and use QFileDialog.getOpenFileName() to open a file dialog for the user to select an image file. If a file is selected (i.e., if filename is not an empty string), we call convert_to_icon to convert the image to an icon. Finally, we call sys.exit(app.exec_()) to ensure a clean exit when the application is closed.&lt;/p&gt;

&lt;p&gt;This script provides a simple way to convert images to icons using PyQt5 and PIL. It can be easily extended or integrated into other projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Round-Up &amp;amp; Kickers
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A Nod to Efficiency&lt;/strong&gt;&lt;br&gt;
Here's the kicker: I've integrated a resource monitor to keep an eye on memory usage. Why, you ask? Well, I'm also working on this cool data analysis project (&lt;a href="https://github.com/MAKaminski/data_analysis"&gt;check it out&lt;/a&gt;) and being resource-efficient is my jam!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZXD-IPp5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ujvovkqf34jzpuw04t5m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZXD-IPp5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ujvovkqf34jzpuw04t5m.png" alt="Resource Manager" width="634" height="657"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Name: &lt;a href="https://marketplace.visualstudio.com/items?itemName=kaih2o.python-resource-monitor"&gt;Python Resource Monitor&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Look and Feel&lt;/strong&gt;&lt;br&gt;
Right now, VisionQuest rocks a transparent blue UI. It's like looking through a futuristic window where you can chat with AI. Pretty neat, huh?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--U5FClA-N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lbj8qc1iczdsfpucvk7i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--U5FClA-N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lbj8qc1iczdsfpucvk7i.png" alt="Look and Feel" width="800" height="216"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What’s Next? Oh, The Plans I Have!&lt;/strong&gt;&lt;br&gt;
I’m planning to split the code into neat little compartments (because organization is key), separate the UI from business logic (they need their own space), and tuck away those API keys in a safe config file.&lt;/p&gt;

&lt;p&gt;And here’s a list of touch-ups and future fireworks I'm planning:&lt;/p&gt;

&lt;p&gt;1) Get that countdown number to shine on its own in the toolbar – no more pyqt5.qtcore.qttimer gatecrashing the party.&lt;br&gt;
2) Add some cool transparency to all text boxes – it's all about that sleek look.&lt;br&gt;
3) Dress up the 'Send Prompt' button in a dapper dark blue.&lt;br&gt;
4) Fix the time calculations – let's keep it simple, no more math gymnastics.&lt;br&gt;
5) Modify frequency options to seconds in all locations&lt;br&gt;
6) On startup, VisionQuest will ask for your goals – a chat app that cares about your ambitions!&lt;br&gt;
7) And the big one: a Lucid chart mapping out multi-goals, progress measurement, and comparing your forecasted performance with actual progress.&lt;/p&gt;

&lt;p&gt;It's going to be like a control center for your aspirations!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wGLqJioC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/40sro8cuszqvb6ap6kj2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wGLqJioC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/40sro8cuszqvb6ap6kj2.png" alt="Project Updates" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wrapping Up&lt;/strong&gt;&lt;br&gt;
So, that's the scoop on VisionQuest. It's more than a chat app; it's my personal lab experiment where I get to mesh code, design, and AI chats into something unique.&lt;/p&gt;

&lt;p&gt;I'd love to hear your thoughts, suggestions, or any cool ideas you might have. Let's make coding not just about typing away in solitude but about sharing, learning, and growing together!&lt;/p&gt;

</description>
      <category>ui</category>
      <category>productivity</category>
      <category>python</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
