INSTALL: remove PropelAuth mentions, add Claude.ai connector section - #1
Merged
Merged
Conversation
Customers never experience PropelAuth as a brand — they see a ShiftControl sign-in. Rewriting the intro paragraph and the Claude Desktop first-use note to match what the user actually sees. Also adds a new Claude.ai (web + mobile) section using the Customize → Connectors flow, between the Claude Desktop and Claude Code sections. Mirrors the section added to shiftcontrol.io/llms-install.txt.
|
⏳ I'm reviewing this pull request for security vulnerabilities and code quality issues. I'll provide an update when I'm done |
|
✅ I finished the code review, and didn't find any security or code quality issues. |
Customers don't think in 'audit log' terms — that's internal vocabulary. The
practice of appending a short note to the app's 'notes' field on each change
stays (it's useful context for whoever looks at the record next), but the
framing shifts from 'audit trail' to 'short context note for the next reader.'
Concrete changes:
- CONTRIBUTING.md: drop the 'Audit trail in notes' rule entirely (Dan's call —
it was over-prescribed; the skills naturally do the right thing without
elevating it to a hard rule). Remaining rules renumber 1, 2, 3.
- README.md: 'audit-logged' / 'audit-friendly' / 'audit notes' → context-note
framing focused on the next-human-to-read-this-app.
- SKILL.md: 'audit log' → 'change history'; 'audit line' → 'new line'; the
notes-line example no longer includes 'via skill vX.Y.Z' — humans reading
the record don't care about the skill version.
- proposal-format.md: '{audit string}' → '{note string}'; 'audit reviewer'
→ 'someone reviewing the record later'.
- vendor-name-mapping.md: 'auditable' → 'leaves a clear record'.
Kept (user-facing verbs, not internal jargon):
- README 'audit spend' — user-facing verb.
- SKILL.md trigger phrase 'Audit our subscription data' — that's how users
actually phrase the request.
Annual contracts dominate SaaS pricing. A 90-day default window risks missing
the most recent renewal invoice for any app billed annually — i.e. half the
user's apps, in practice. 18 months covers every cadence (monthly / quarterly
/ annual) plus a buffer to confirm you have the latest invoice.
If the user wants a narrower window ('just this month', 'last quarter'),
the skill honors that. Wider than 18 months stays rare and explicit, since
old invoices are usually superseded.
Updates:
- SKILL.md Step 2: 90 days → 18 months, with rationale.
- SKILL.md sample proposal: 'no invoice in the last 90 days' → '... 18 months'.
- references/invoice-detection.md: rewritten Time-Window section reflecting
the new default and dropping the '400 days for annual contracts' clause
(now subsumed by the 18-month default).
Mid-period invoices (seat additions, plan upgrades, prorated adjustments) are partial charges. Computing per-user cost from them produces silently- wrong numbers. The skill now classifies invoice type first, then extracts only the fields that are reliable for that type. SKILL.md Step 4 changes: - New 'Classify the invoice type first' subsection at the top - Incremental signals listed inline (subject/body text, amount, service period, position relative to a renewal invoice) - Per-unit cost and billing frequency are explicitly marked 'Full only' - Plan/tier and contract end date marked as STILL usable from incrementals (they are often the BEST source for plan changes specifically) - 'Only incrementals for this vendor' is now its own surfacing path invoice-detection.md adds: - Full vs incremental classification section with both sides' tells - Field-by-field reliability matrix - Multi-invoice combining rules (full + incrementals → use each for its strongest signal; only-incrementals → skip cost, propose plan + dates) - Annual-vs-monthly cost normalization note (separate but adjacent gotcha where an annual invoice quotes $84/user but storage is $7 + 'year')
…ce handling Five changes Dan flagged: 1. Notes are REPLACED, not appended. The skill runs repeatedly; one 'Updated from X dated Y' line per app at all times. Match pattern is 'line starts with "Updated from " and contains "invoice dated <YYYY-MM-DD>"'; user-written content in the notes is preserved. 2. Reseller annotation in the note string. Slack-via-Salesforce and Google-Workspace-via-ShiftControl are the canonical examples — note becomes 'Updated from Slack invoice dated 2026-03-15 (billed via Salesforce)' so the next reader understands why the From-address didn't match the product. 3. Slack-via-Salesforce (and Heroku-via-Salesforce, future Figma-via-Adobe) added to vendor-name-mapping.md as 'acquired-product domain rebinds'. Body content is authoritative; From-domain can mislead. 4. PDF invoices now PARSED (not skipped). Most email MCPs return attachment contents; most AI assistants can read PDFs natively. Removed PDF from the 'v0.1.0 doesn't do' list. Brief mention in SKILL.md Step 4; details in invoice-detection.md. 5. Web-hosted invoices: skill ASKS the user before following 'click here to view your invoice' links. Suggested phrasing is in the doc. If the user agrees AND the assistant has a fetch capability, fetch + parse. Removed from 'v0.1.0 doesn't do' list. Display change in the proposal block: 'note will be added:' → 'notes update:'. The new phrasing is more honest about replace-not-append semantics and is intentional per proposal-format.md. Reseller line-item matching gets its own subsection in vendor-name-mapping.md (rule 4a) — one reseller invoice with Google Workspace + JumpCloud line items becomes two separate ShiftControl updates.
Without CODEOWNERS, branch protection's PR-review requirement enforces '1 approver' but doesn't route the request to a specific reviewer. With this file, GitHub auto-requests review from @shiftcontrol-dan on every PR that touches anything in the repo. The 'require code owner reviews' flag on the branch protection rule was intentionally left off in the initial setup (since CODEOWNERS didn't exist). Now that CODEOWNERS lands, that flag can be flipped on if you want to require code-owner approval specifically (rather than 'any 1 approver').
Mined 18 months of a real ShiftControl invoice inbox to verify and refine the detection / matching guidance. Three corrections and several additions. CORRECTIONS: - Slack-via-Salesforce was overstated. Real accounts continue receiving Slack invoices from feedback@slack.com even post-acquisition. Removed the hard claim; phrased as 'pattern varies per acquisition and per account, dont assume, read the body.' ADDITIONS to vendor-name-mapping.md: - Real-world vendor-to-sender catalog: Slack, Notion, GitHub, Granola, Framer, Anthropic, 1Password, JumpCloud, Cloudflare, Zoom, AWS. Includes typical subject patterns and format (inline / PDF / Stripe-template). - Stripe-template receipts recognized as a vendor-cluster pattern (Granola, Framer, Anthropic all share 'Your receipt from <Vendor> #<NUMBER>'). ADDITIONS to invoice-detection.md: - Dedicated invoice mailbox + forwarding rules: many orgs route invoices to invoices@<company>.io via forwarding. Sender appears as the alias; real vendor is in the body. Skill must read body to identify the vendor in these cases. - False-positive cluster: outbound payments (Workato-paying-you via Coupa Pay is the textbook example, but also customer payments via Stripe / Sequence HQ, professional services, one-time SSL/domain/hardware, telecom, bank / tax / regulatory). - Reseller catalog: Ingram Micro Asia (Imcloudservicedesk.hk@cloud.im), uses 2026SIHK<NUMBER> invoice format and sends Credit Memos (reduce balances, do not parse as new charges). AWS Marketplace for third-party SaaS. ADDITION to SKILL.md Step 3: - Outbound-payments filter - Dedicated-mailbox forwarding callout (read body for vendor)
Three things in one commit:
1. AUTO-RELEASE WORKFLOW (.github/workflows/release.yaml + top-level VERSION)
Modeled on the google_api_sdk pattern. On push to main where VERSION
changes (or workflow_dispatch), the workflow:
- Reads VERSION (validates SemVer)
- Skips if v<VERSION> tag already exists
- Imports a CI signing GPG key (secrets.GPG_PRIVATE_KEY/_PASSPHRASE)
- Creates a signed tag v<VERSION>, pushes it
- Builds per-skill zips (skills/<name>/ → <name>-v<VERSION>.zip)
- Creates a GitHub release with --generate-notes + zip artifacts
VERSION pinned to 0.1.0 to match the existing tag; the workflow will
detect the existing tag and skip on first deploy, then activate cleanly
on the next bump.
CONTRIBUTING.md documents the GPG setup steps (gpg --gen-key, export
private to GPG_PRIVATE_KEY, public to GitHub signing keys).
2. SALESFORCE-BILLED SLACK — both patterns coexist
Concrete evidence (a real Salesforce invoice .eml for Slack annual):
- From: Salesforce APAC Billing <billing@apac.salesforce.com>
- Subject: 'salesforce.com Invoice <NUMBER>, <Customer Org>'
- Body does NOT mention Slack; product is only in the PDF attachment
- PDF filename: 'Salesforce_Invoice_<NUMBER>.pdf'
Documented as: SMB / monthly contracts continue billing from
feedback@slack.com; enterprise / annual contracts move to Salesforce.
This is the canonical case for why PDF parsing matters. Other
Salesforce-billed products on the same pattern: Heroku, Tableau,
MuleSoft, ExactTarget.
3. INGRAM IS A DISTRIBUTOR (direction matters)
Real Ingram traffic in a ShiftControl-style inbox is mostly NOT for
products the org consumes — it's invoices for products the org SELLS
THROUGH Ingram to downstream customers. Only invoices where the org is
the listed 'Bill To' / 'Customer' / 'Sold To' represent real
subscription costs.
Detection signal: parse the body / PDF for the customer-side field.
Match the org → propose update. Different name → outbound channel sale
→ filter out, surface in 'channel / resale invoices skipped' section.
Credit Memos and payment-received notifications: don't propose changes
from these regardless of direction. They modify balance, not per-unit
cost. Mention informationally.
4. CORRECTION: shared billing inboxes are GROUPS, not forwarding rules.
Earlier text said the invoices@ alias appears as the SENDER because of
forwarding rules — that's wrong. The pattern is:
- invoices@<company>.io is a Google Group / DL on the RECEIVER side
- Each SaaS subscription's billing-contact email is set to that address
- Invoices arrive with the actual vendor as From and the group as To
- Gmail's API sometimes shows the group as From when a personal mailbox
reads via group membership (a display quirk, not a forwarding rule)
SKILL.md Step 3 and invoice-detection.md updated to reflect this and to
guide the skill toward 'to:invoices@<their-company>.io newer_than:18m'
as the highest-precision starting search.
GPG_PRIVATE_KEY and GPG_PASSPHRASE are configured as ShiftControl-io org-level secrets (already backing release workflows in other repos like google_api_sdk). No per-repo setup needed; both secrets are automatically available to this repo's release.yaml. Replaced the 'CI signing key setup' how-to with a brief note pointing at the org secrets, plus a section for forkers who'd need their own equivalent setup outside ShiftControl-io.
Org-level secrets are an infra concern, not something a skill contributor needs to know about. The release.yaml workflow references the secrets directly; that's enough.
shiftcontrol-krishna-nanduri
approved these changes
May 14, 2026
5 tasks
shiftcontrol-dan
added a commit
that referenced
this pull request
May 14, 2026
* Bump VERSION to 0.2.0 PR #1 brought substantial changes since v0.1.0 — bumping to 0.2.0 to trigger the new auto-release workflow and produce a v0.2.0 tag + release with the current skill content. What landed since v0.1.0 (per PR #1): - Multi-platform install — new Claude.ai (web + mobile) connector section alongside Claude Desktop, Claude Code, Cursor, Windsurf, Cline, Continue.dev, ChatGPT, Gemini CLI - refresh-subscription-info default search window: 90 days → 18 months (catches annual renewals) - Full vs incremental invoice classification — prevents silent wrong per-user-cost extraction from prorated charges - Notes replace-not-append — single 'Updated from X invoice dated Y' line per app, doesn't accumulate across monthly re-runs - PDF parsing enabled (canonical case: Salesforce-billed Slack where product is only in the PDF) - Web-hosted invoices: skill asks before fetching - Real-world vendor catalog covering Slack, Notion, GitHub, Granola, Framer, Anthropic, 1Password, JumpCloud, Cloudflare, Zoom, AWS - Distributors-vs-resellers direction handling (Ingram = distributor; Bill-To field disambiguates consumption vs channel sale) - Shared billing inbox correction (Google Group on the receiver side) - Outbound-payments and other false-positive filters - CODEOWNERS routing to @shiftcontrol-dan - INSTALL/README marketing-voice rewrite, audit-log terminology removed - Auto-release workflow itself (.github/workflows/release.yaml) — what this VERSION bump exercises for the first time Merging this PR triggers release.yaml, which signs and pushes a v0.2.0 tag and creates a GitHub release with the per-skill zip attached. * validate-skills: drop PR path filter so the check always reports Branch protection on main requires the 'validate' status check, but the previous path filter ('skills/**') meant the workflow skipped on PRs that didn't touch skill files (like #2, which only bumps VERSION). A skipped required workflow leaves the check pending forever, blocking merge. Fix: run on every PR into main. The workflow is cheap (yq frontmatter checks + a few greps), so the cost of always running is negligible. The push trigger keeps its path filter — no need to re-run validate on main pushes that didn't touch skills.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Customers never experience PropelAuth as a brand — they see a ShiftControl sign-in screen. This PR rewrites the two PropelAuth mentions in
INSTALL.mdto describe what the user actually sees, and adds a Claude.ai (web + mobile) connector section that mirrors what's now live onshiftcontrol.io/llms-install.txt.Changes
### Claude.ai (web + mobile)with the Customize → Connectors flowType of change
Checklist
Test plan
grep -i propelauthreturns zero hits across*.md/*.yaml/*.json/*.txtin the repoNotes for the merge
This is also the first-PR shakedown for this repo's CI. After approving, watch for the
validateandverifychecks to pass green before merging — that proves branch protection + signed-commit enforcement work end-to-end on a real PR. If either check fails, that's a CI bug to fix, not a doc problem.