Skip to content

Releases: shalintripathi/organic-os

organic-os v0.7.0 - the loop reads richer data

Choose a tag to compare

@shalintripathi shalintripathi released this 07 Sep 19:59

organic-os reads evidence from GA4, GSC, WordPress, and the open web. That list has real gaps: keyword volume needs a Google Ads developer token most users do not have, rank tracking does not exist (GSC only reports queries where the site already surfaced), and competitor keyword or backlink data is out of reach on principle - we do not scrape.

v0.7.0 fills those gaps with an optional second adapter in the search-data capability slot: OpenSEO (MIT, DataForSEO-backed), which exposes its data over MCP. Decision recorded in ADR-0012.

When its tools are present in the session:

  • hoo-keyword-intel hydrates ideas with volume, difficulty, and CPC - keyword intelligence without a Google Ads token, the tier most users were missing.
  • The weekly's keyword portfolio records true SERP position per tracked keyword alongside the GSC average. GSC sees where you surfaced; the adapter sees where you did not, which is the whole point on an early-stage site.
  • hoo-competitor-intel reads competitor ranking keywords and backlink profiles from licensed data - the no-scraping rule (ADR-0006) untouched.
  • hoo-citation-tracker gains an AI-visibility source.

When they are absent, every skill behaves exactly as before, and says so honestly rather than fabricating.

You run OpenSEO yourself - self-hosted or their hosted account - with your own DataForSEO key. organic-os stores no new credential, adds no env var, and makes no network call of its own. The tier is read-only against your OpenSEO account: nothing in the loop creates rank trackers or spends your budget.

Also in this release: core.keywords, the single writer for keywords/tracking.yaml - the last 'edit the YAML by hand' instruction in the skills is gone, and an empty tracked set can now be seeded from the profile's keywords through the normal approval gate.

488 tests, 9 audit checks, 8 gate probes, CI green. Two tasks carried from the same study are open for contributors: a deliberately broken audit fixture (good first issue) and the skills-registry distribution investigation.

organic-os v0.6.1 - community wave two, hardened under review

Choose a tag to compare

@shalintripathi shalintripathi released this 09 Aug 18:51

Three community PRs from @waterlemonnn, each adversarially reviewed and mutation-tested before merge:

  • registry.unregister() (#24, closes #1) - /organic-os:reset used to instruct a hand edit of sites.yaml, re-implementing the atomic-write + chmod pattern in prose. It now calls the one writer lib/core is supposed to be: unknown-slug guard before any write, active pointer cleared when the removed site held it.
  • Printable-ASCII token guard (#23, closes #20) - a zero-width space, BOM, or accented letter in a mispasted Telegram token now fails fast at the sanitizer with the reason named, instead of deeper in the HTTP stack. The error message is a constant; the token can never appear in it.
  • All three CI checks documented (#22, closes #4) - verify-gates.sh ran in CI but was missing from CONTRIBUTING's pre-PR steps, so a contributor following the docs exactly could be green locally and still fail CI.

The review process earned its keep twice this release. Mutation testing showed #24's permissions test passed without exercising the code it claimed to test (a plain write survived, because the file was already 600 from an earlier call); the test is now discriminating, verified failing against the mutant and passing against the real code. And the same review surfaced a pre-existing defect nobody had reported: the registry's atomic write left its tmp file at default-umask permissions before the final chmod, so site names and brain paths briefly sat world-readable. The tmp file is now 0600 for its whole life, stale-tmp-after-crash case included.

477 tests, 9 audit checks, 8 gate probes, CI green.

organic-os v0.6.0 - the loop speaks to a site's actual stage

Choose a tag to compare

@shalintripathi shalintripathi released this 30 Jul 19:13

Every detector this project ships assumes an established site. Striking distance looks for positions 4 to 15. Content decay needs 50 clicks in the older window. Anomaly alerts skip any metric whose median is under 10. Cannibalization needs two pages sharing a query with real volume.

A new site clears none of those bars. So the loop would run every day, find nothing that met a threshold, and report 'quiet day, nothing sent' - while sitting on genuinely readable data: indexed pages, live queries, positions climbing. Twelve days of that reads exactly like a broken product, and every site starts there.

Now the daily and weekly classify the site's stage from its own data and speak to it.

On an early-stage site they report which queries the site is visible for at all, the closest-to-breakthrough opportunities banded by position - top, page two, visible, distant - each with the lever that plausibly moves it, and an explicit line that zero clicks at those positions is expected rather than a fault. The dormant detectors are named alongside the threshold that wakes each one, so the silence is explained instead of mysterious. A note describes a lever, never a promised position change: the evidence discipline that applies to everyone else's claims applies here too.

Early sites get the channel summary weekly rather than daily, because a daily 'still climbing' is noise. They always get an alert the moment a query appears that the site was not visible for before, because on a new site that is the real progress signal.

Growing and established sites are unaffected. Their behaviour is unchanged.

464 tests, 9 audit checks, 8 gate probes, CI green.

organic-os v0.5.5 - the queue rebuild no longer dies quietly

Choose a tag to compare

@shalintripathi shalintripathi released this 30 Jul 18:59

A follow-on to 0.5.4, found by running the fix against a real brain instead of trusting it.

content-engine writes a draft sidecar next to the brief it belongs to. The queue rebuild globbed every markdown file in the folder, read the sidecar as an item, and raised on its missing status field. On its own that was a crash. Combined with the best-effort wrapper added in 0.5.4 - which exists so a bookkeeping refresh can never block a real state transition - it became worse than a crash: any brain containing a drafted brief would swallow the error and stop refreshing its queue permanently, with no sign anything was wrong.

Drafts are now skipped, since a draft is not an item. An item that parses but lacks the frontmatter every item is born with is reported as malformed rather than raising, because this code runs inside a swallow-everything guard and an exception there is invisible by construction.

The lesson is the release note: a best-effort wrapper turns a loud failure into a silent one. Anything running inside one has to be defensive on its own terms.

441 tests, 9 audit checks, 8 gate probes, CI green.

organic-os v0.5.4 - the loop is visible again

Choose a tag to compare

@shalintripathi shalintripathi released this 30 Jul 18:54

Two bugs found by diagnosing a live instance that ran unattended for twelve days. Both are the same failure in different clothes: the loop worked, and the operator could not tell.

  • The approvals queue could go stale. queue.md is a derived file, and only the CLI refreshed it, so any skill calling set_status directly left it frozen. On the instance in question it had been wrong for six days: it still listed briefs as pending that had already been approved, drafted, and published to WordPress. Reading it, you would conclude nothing was moving. It now refreshes on every status change, best-effort, so a bookkeeping refresh can never block or roll back a real state transition.
  • Notifications could be skipped in silence. Sending was described in skill prose as a three-step dance - check whether it was sent, send it, mark it sent - and prose steps get missed. On the same instance every item read as never notified while the channel was reachable the entire time. approval.notify_pending now sends and marks in one call: an item is marked only after the send succeeds, so a failure is retried on the next run instead of vanishing. Sending twice is impossible; losing a send is no longer silent.

A working system that cannot show its work is indistinguishable from a broken one. That is the bug this release fixes.

440 tests, 9 audit checks, 8 gate probes, CI green.

organic-os v0.5.3 - the link renderer can no longer be steered

Choose a tag to compare

@shalintripathi shalintripathi released this 24 Jul 19:31

One security fix, found by reviewing a community PR that was itself safe.

The Monday report's link renderer emitted href values with attribute escaping disabled and no scheme check. Report content includes brain data - titles, reasoning, URLs - so a crafted link could break out of the attribute (a double quote followed by an injected handler) or ship a clickable javascript: or data: link into HTML that goes to Telegram, email, and PDF.

Now: URLs are attribute-escaped, and schemes are allowlisted - http, https, mailto, and relative paths render as links; anything else renders its label as plain text with the URL dropped. The check normalizes case and strips control characters first, so JaVaScRiPt: and tab-prefixed tricks do not slip past. Six new tests pin the escaped output itself, mutation-verified against the unfixed renderer.

Closes #19. Found during the review of #18 (which was clean - this defect predates it and sat in a line that PR never touched). The remaining sanitizer-completeness follow-up is tracked in #20.

428 tests, 9 audit checks, 8 gate probes, CI green.

organic-os v0.5.2 - community fixes

Choose a tag to compare

@shalintripathi shalintripathi released this 23 Jul 18:34

Two reviewed outside contributions plus a small robustness fix. Both PRs were checked two ways in isolated worktrees - an independent correctness pass and an adversarial pass - before merge.

  • Inline code spans in the Monday report. Backtick-wrapped text (item ids and the like) now renders as code instead of arriving with literal backticks. The renderer escapes content before wrapping it, so a report carrying brain data stays injection-safe, and the tests assert the escaping itself. Contributed by AshSgDe29071999 in #18, closing #7.
  • DEL and C1 control characters are now rejected by sanitize_bot_token. The earlier guard caught control chars below 0x20 but missed DEL (0x7f) and the C1 range, so those could reach the request layer. Verified it cannot false-positive on a real token. Contributed by MasRama in #16, closing #15.
  • approval.find() no longer aborts on a malformed sibling brief file; the bad file is skipped and the scan continues.

Two follow-ups from the reviews are tracked as good-first-issues: a pre-existing link-renderer href injection (#19) and tightening the token guard to allow only printable ASCII (#20).

422 tests, 9 audit checks, 8 gate probes, CI green.

organic-os v0.5.1 - inline approval buttons, and knowing whether your update landed

Choose a tag to compare

@shalintripathi shalintripathi released this 22 Jul 22:48

Two things: a Telegram UX upgrade that was already on main, and tooling so an update that does not take is visible instead of silent.

  • Inline Approve/Reject buttons in Telegram. Approving no longer means typing 'approve ' - the proposal arrives as a readable message (title, framing, a reasoning excerpt) with tap-to-approve and tap-to-reject buttons. Over-long ids fall back to the typed grammar rather than shipping a button that cannot resolve, the redaction advisory is applied to the rendered text, and the typed and reply-context grammars still work unchanged.
  • /organic-os:diagnose now reports update currency. It reads the version you are running and the latest published version, and tells you plainly whether you are current, behind, or ahead - with correct semver ordering, so 0.5.10 is newer than 0.5.9. This is an on-demand read of an already-public version number, not telemetry: nothing about you is sent, and the diagnostic itself is never transmitted.
  • A troubleshooting section for the update that reports success but does not change the running version. This is a Claude Code and Cowork plugin-manager behavior, not something a plugin can fix from inside its own update - the honest remedy is a clean reinstall (CLI) or remove-and-re-add (Cowork), which loses nothing because your brain and config live outside the plugin. Worth a /bug to the Claude Code team.
  • release-check, a maintainer step that fails when plugin code has changed since the last tag but the version was not bumped. That exact gap is why the inline-buttons work sat on main invisible to the updater under an unchanged 0.5.0 - the updater compares version numbers, so new code only reaches users when the number moves.

415 tests, 9 audit checks, 8 gate probes, CI green.

organic-os v0.5.0 - memory integrity and honest boundaries

Choose a tag to compare

@shalintripathi shalintripathi released this 22 Jul 22:09

Patterns adopted from Garry Tan's gstack (https://github.com/garrytan/gstack), filtered through this project's own philosophy. Studying it exposed three gaps that were real in our code, not hypothetical.

  • Durable decisions, consulted before re-deciding. The brain created a decisions/ directory and then never wrote to or read it, so the loop could re-propose work a human had already rejected, forever. Rejecting an item now records a searchable decision with the reason, and the skills that create work search it first. Re-proposing is allowed; doing it silently is not - the proposal must say what changed since the rejection.
  • Lessons expire by evidence tier. Skillbook entries always carried a last-confirmed date that nothing ever acted on. Anecdotal lessons now go stale at 90 days, moderate at 180, strong at 365, and the weekly reflection surfaces them for re-validation. Nothing is ever auto-deprecated; a human decides.
  • An advisory redaction scan before outbound content. Six sinks pushed brain content outward with no guard. Findings are tiered and excerpts are masked, and the docs are blunt about the limit: this reports, it does not prevent. A high-tier finding means a credential already left.
  • /organic-os:diagnose, the honest alternative to telemetry. It assembles exactly the report telemetry would have sent, scans its own output first, reports credentials only as present or absent, and then prints it for you to paste or discard. It never transmits. There is still no telemetry in this project and ADR-0011 records why, along with the trigger that would justify revisiting.
  • Causes require the check that was run. A cause may not be asserted without naming the comparison actually performed. If it was not run, the cause is unknown - seasonality is not an explanation unless somebody compared the same weekday in the prior period.
  • /organic-os:verify-outcome recomputes a claim independently. It reads only the item, the URL and the date windows, pulls the raw data, computes the delta, and compares to what was claimed afterwards. Divergence is recorded as a finding. The standard evidence.md applies to other people's claims now applies to our own.

404 tests, 9 audit checks, 8 gate probes, CI green.

organic-os v0.4.4 - completing the token-sanitization fix

Choose a tag to compare

@shalintripathi shalintripathi released this 22 Jul 18:39

Follow-up to #13, which closed a real token-leak path (27 leaking cases reduced to 1 pre-existing and unreachable, verified by a 67-probe battery against real urllib). The security fix was sound and merged; these are the four defects that review surfaced around it.

  • A test that passed for the wrong reason. A local variable named http shadowed the http module, so http.client.InvalidURL raised AttributeError instead and the handler swallowed it - the test went green without ever exercising the path it named. Renamed the local; reverting the source now fails it with the real InvalidURL.
  • A vacuous assertion whose right-hand operand was always true, so it could never fail. Replaced with three independent assertions, demonstrated against a partial-leak message that the old form accepted.
  • The sanitizer wiring was unpinned. Stripping sanitize_bot_token from all three call sites left the suite fully green; six new parametrized tests now fail if any call site drops it.
  • Sanitized errors now name the failure class. Collapsing a malformed response, a caller bug, and a bad URL into one opaque string protected the token but cost real debuggability. The message now carries the exception class name, which can never contain a token, while the URL and original message stay suppressed.

Credit to kevinnft for #13, the third merged contribution today, and for a fix that only became possible because of the previous one.

313 tests, 9 audit checks, 8 gate probes, CI green.