Skip to content

feat: priority-based rules sections with hard/soft model#2

Merged
Jeremy8776 merged 2 commits into
masterfrom
feat/rules-priority-sections
May 18, 2026
Merged

feat: priority-based rules sections with hard/soft model#2
Jeremy8776 merged 2 commits into
masterfrom
feat/rules-priority-sections

Conversation

@James-Chapman
Copy link
Copy Markdown
Collaborator

@James-Chapman James-Chapman commented May 18, 2026

Priority-based rules sections (hard/soft) replacing the flat-string model.

Model

  • Coding/General: hard + soft priorities. Soul: soft-only.
  • Legacy flat strings auto-migrate on read
  • Old preference/style sub-fields merge into soft with ## Preference / ## Style headers

Server

  • validateRules accepts only hard/soft per section
  • normalizeRules() converts both legacy formats to canonical; flattenSection() helpers render per-adapter

UI

  • Section textareas render per allowed priority (hard/soft)
  • Predefined profiles use new format

Tests

  • validation-smoke.js updated for new priority model

@James-Chapman
Copy link
Copy Markdown
Collaborator Author

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 59c6a52594

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread server/server.js Outdated
// Local API auth: if a CE_API_KEY is configured, require a Bearer token
// on every API request. Static UI files and auth endpoints are exempt.
const token = getAuthToken();
if (token && url.pathname.startsWith('/api/') && !url.pathname.startsWith('/api/auth/')) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Enforce bearer auth on token-mutation auth endpoints

The new auth gate explicitly skips every /api/auth/* route, which lets any local caller mutate auth state without credentials. In practice, an unauthenticated process can call /api/auth/remove (or rotate via /api/auth/generate) and then access protected /api/* data, so the protection is trivially bypassed in the same threat model this feature is meant to defend (other local processes).

Useful? React with 👍 / 👎.

Comment thread server/lib/rule-files.js Outdated

/** @param {string} name @returns {RuleData | null} */
function readRuleFile(name) {
const filePath = path.join(RULES_DIR, `${name}.json`);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Sanitize rule file names before filesystem reads

readRuleFile joins untrusted name directly into a path without applying sanitizeName or a base-dir containment check. Because the route decodes path components first, inputs like %2e%2e%2f... become ../... and can escape data/rules, allowing reads of arbitrary *.json files via /api/rule-files/:name (and via combineRuleFiles in publish), which is a path traversal data exposure.

Useful? React with 👍 / 👎.

Comment thread ui/rules-lab.js Outdated
currentRuleFile = result.name;
if (input) input.value = '';
await loadRuleFileList();
RulesLab.setDraft(emptyRules);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Load newly created rule file into ConfigTab state

After creating a rule file, the UI only calls RulesLab.setDraft(emptyRules) and never refreshes ConfigTab’s _currentServerData for that file. The next save therefore merges non-active priorities from the previously loaded file (stored = _currentServerData || RS.get()), which can silently copy old rule text into the new file on first save.

Useful? React with 👍 / 👎.

Comment thread ui/store.js Outdated
returnErrors: true,
});
if (result?.ok) return result;
return { ok: true };
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Return write failures from rule-file update API wrapper

DS.updateRuleFile reports { ok: true } even when the backend request fails. That makes the caller treat failed writes (for example 401/auth failures or server-side validation errors) as successful, so the UI says the rule was saved while disk state on the server is unchanged.

Useful? React with 👍 / 👎.

Comment thread server/server.js Outdated
// Local API auth: if a CE_API_KEY is configured, require a Bearer token
// on every API request. Static UI files and auth endpoints are exempt.
const token = getAuthToken();
if (token && url.pathname.startsWith('/api/') && !url.pathname.startsWith('/api/auth/')) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep built-in key management reachable after enabling API auth

The new global auth check now requires bearer auth on /api/keys/*, but the Config tab still calls those endpoints with raw fetch('/api/keys...') and no Authorization header. Once a user enables API auth, the built-in API-key status/save/remove flows start returning 401, so users can no longer manage Anthropic keys from the UI.

Useful? React with 👍 / 👎.

Comment thread server/server.js Outdated
const token = getAuthToken();
if (token && url.pathname.startsWith('/api/') && !url.pathname.startsWith('/api/auth/')) {
const authHeader = String(req.headers.authorization || '');
const provided = authHeader.startsWith('Bearer ') ? authHeader.slice(7) : '';
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3 Badge Accept case-insensitive Bearer auth scheme

The token parser only accepts headers starting with exact 'Bearer ' casing. HTTP auth schemes are case-insensitive, so valid headers like authorization: bearer <token> are rejected with 401 even when the token is correct, creating avoidable client interoperability failures.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner

@Jeremy8776 Jeremy8776 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review

Thanks for the work here, James. This adds substantial value — the test coverage and API auth are both overdue. A few things to address before merging.

1. Priority model: preference / style → soft loses semantics

migrateRules() concatenates the old preference and style fields into the single new soft field. Those were distinct concepts — one was "prefer this but not required", the other "stylistic guidance". Concatenating them discards that distinction. Either preserve them as separate fields or document this as an intentional simplification.

2. This PR is doing too much

Three distinct concerns in one PR:

  • Rules priority model refactor (breaking data change)
  • 12 test suites + API endpoint tests
  • API auth + security fixes
  • Projects publish UI + grid/list toggle
  • Nav reorder (Rules before Skills)

Please split at minimum the rules model change from everything else. A data model migration deserves its own focused review cycle.

3. Third commit message is too vague

Some security related updates doesn't describe what changed or why. Please expand.

4. CSS coupling: Projects reuses .handoffs-list / .handoff-card

Projects tab markup (ui/projects.js) relies on CSS selectors from tab-handoffs.css. If handoffs styling is ever refactored, projects will break silently. Either rename/copy the selectors or import a shared base class.

5. docs/how-it-works.md — unrelated addition

A 270-line doc file is included with no clear connection to any of these changes. Does it belong here?

None of these are blockers individually, but together they make the diff harder to review and risk regressions. Happy to re-review once addressed.

@James-Chapman James-Chapman force-pushed the feat/rules-priority-sections branch from 59c6a52 to aca6648 Compare May 18, 2026 10:55
@James-Chapman James-Chapman changed the title feat: priority-based rules, 12 new test suites, API auth, and security fixes feat: priority-based rules with configurable sections, 12 new test suites, and API auth + security fixes May 18, 2026
… general, preference-only for soul

Data model change:
- rules.json: coding/general are now {hard, preference, style} objects
  instead of flat strings; soul is now {preference} instead of string
- Backward compatible: server accepts both old flat-string and new
  priority-object formats; legacy data is auto-migrated on read

Server changes:
- server/lib/validation.js: validateRules accepts nested priority
  objects with section-specific allowed priorities
- server/compiler.js: normalizeRules() converts legacy flat strings
  to {preference: 'text'} for backward compatibility; all adapters
  use flattenSection/flattenSectionLabeled helpers

UI changes:
- rules-lab.js: removed single-priority dropdown per section; each
  coding/general card now has 3 labeled textareas (Hard rule,
  Preference, Style guidance); soul has a single Preference textarea
- config.js: save/load reads/writes nested priority objects; handles
  both legacy and new formats via migrateRules()
- store.js: RS.get() and RS.loadFromServer() auto-migrate legacy
  flat-string data to priority-object format
- data.js: DEFAULT_RULES updated to nested format
- tab-config.css: added .rules-priority-group, .rules-priority-section,
  .rules-priority-label styles

Tests:
- validation-smoke.js: added 8 new tests for priority-object validation,
  including soul rejecting non-preference priorities, mixed flat/object
  formats, and array values
@James-Chapman James-Chapman force-pushed the feat/rules-priority-sections branch from 81deb20 to e5d5101 Compare May 18, 2026 11:05
@Jeremy8776 Jeremy8776 merged commit f1d39fc into master May 18, 2026
@James-Chapman James-Chapman changed the title feat: priority-based rules with configurable sections, 12 new test suites, and API auth + security fixes feat: priority-based rules sections with hard/soft model May 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants