The difficult part is not getting Claude to write the code. The difficult part is knowing whether the result actually works.
This matters in a MegaIndex BrowserAPI project because a change can look correct in the diff and still fail against a real remote browser. The script may connect but leak sessions, pass unit tests while failing over CDP, retry indefinitely, expose the browser connection URL, or return an empty result without treating it as an error.
The less closely you watch the agent, the more important it becomes to replace trust with mechanical checks.
The practical model is simple:
- CLAUDE.md tells Claude how the project should be changed;
- skills define procedures Claude should repeat consistently;
- hooks enforce checks Claude must not be allowed to skip.
The central rule is:
An instruction influences the agent. A hook controls whether the work is allowed to continue.
The real problem is not code generation
Suppose a Node.js service uses Playwright to connect to MegaIndex BrowserAPI. You ask Claude Code to add persistent browser profiles, retry failed navigation, and save structured task results.
The change may affect:
- the BrowserAPI connection module;
- browser context creation;
- profile selection;
- proxy settings;
- task retries;
- session cleanup;
- result validation;
- unit and integration tests.
Claude may produce a clean summary:
Implemented persistent browser profiles. Added retry handling. Updated tests. All checks pass.
That summary does not prove that:
- the remote browser connection was tested;
- the session was closed after an exception;
- the retry limit is respected;
- the same profile is not used concurrently;
- the output contains real data;
- the tests were not weakened to make them pass;
- credentials were not added to logs;
- the agent actually ran the commands it claims to have run.
For unattended work, “Claude said it passed” is not a verification strategy.
Three layers for a reliable Claude Code workflow
The most useful way to organize agent instructions is to separate conventions, procedures, and enforcement.
| Layer | Purpose | BrowserAPI example |
|---|---|---|
| CLAUDE.md | Rules that apply to most project tasks | All BrowserAPI connection values must come from environment variables |
| Skill | A repeatable procedure for a specific type of work | Run tests, inspect the diff, execute a BrowserAPI smoke test, and report evidence |
| Hook | A rule that must not be skipped | Do not let Claude finish while tests fail or new skipped tests appear |
Putting everything into one large instruction file is usually a mistake. Every line competes for the agent’s attention. Long files make individual rules easier to overlook.
A better distribution is:
- stable project conventions go into CLAUDE.md;
- task-specific procedures go into .claude/skills/;
- hard requirements go into hooks.
Start with a small and testable CLAUDE.md
A weak rule looks like this:
Follow best practices when working with BrowserAPI.
It is vague, cannot be tested, and gives Claude no concrete implementation direction.
A stronger rule names the expected behavior:
Read the MegaIndex BrowserAPI connection URL from MEGAINDEX_BROWSER_WS_ENDPOINT. Never place it in source files, test fixtures, screenshots, logs, or committed configuration.
Another weak rule:
Handle browser sessions correctly.
A checkable replacement:
Every BrowserAPI connection must be closed in a finally block. A task must not create a second browser connection when an active connection can be reused safely.
A useful CLAUDE.md for a BrowserAPI project may include rules such as:
# Browser automation rules - Keep BrowserAPI connection code in src/browser/connection.ts. - Read the connection URL only from MEGAINDEX_BROWSER_WS_ENDPOINT. - Never print connection URLs, cookies, proxy credentials, or authorization headers. - Close every browser connection in a finally block. - Add a finite retry limit to every navigation retry loop. - Return browser task results as structured objects, not formatted text. - Treat an empty extraction result as a failure unless the task explicitly allows it. - Put remote-browser integration tests in tests/browserapi/. - Do not delete, skip, weaken, or replace an existing test to make a change pass. - Run unit tests and the BrowserAPI smoke test before declaring the task complete.
These rules are useful because a reviewer can check whether Claude followed them.
Name the replacement, not only the prohibition
A prohibition without an alternative leaves room for improvisation.
Instead of:
Do not hardcode the browser endpoint.
Use:
Read the browser endpoint from MEGAINDEX_BROWSER_WS_ENDPOINT.
Instead of:
Do not use unlimited retries.
Use:
Use the shared retry helper from src/browser/retry.ts and set an explicit maximum attempt count.
Instead of:
Do not return unstructured data.
Use:
Return BrowserTaskResult with status, finalUrl, data, errors, and completedAt.
The replacement tells Claude what correct code should look like.
Do not mark every rule as important
Words such as IMPORTANT only raise a rule relative to quieter instructions around it. If every rule is emphasized, none of them has priority.
For a BrowserAPI project, the strongest emphasis should normally be reserved for a few costly mistakes:
- exposing credentials;
- leaking remote browser sessions;
- weakening tests;
- performing destructive actions against production accounts.
Everything else should be written plainly.
Create a verification skill for BrowserAPI changes
Verification should not depend on remembering to ask Claude whether it ran the tests.
Create a reusable skill that activates after code changes and before completion.
Example file:
.claude/skills/verify-browserapi/skill.md
Example content:
--- name: verify-browserapi description: Run after BrowserAPI-related code changes and before declaring the task complete. --- Verify the change using this procedure: 1. Run the unit test suite. 2. Run the TypeScript type checker. 3. Run the BrowserAPI smoke test against the configured test page. 4. Read the complete git diff. 5. Confirm that no test was deleted, skipped, weakened, or replaced with a less strict assertion. 6. Confirm that no BrowserAPI endpoint, cookie, password, proxy credential, or authorization header appears in the diff. 7. Confirm that browser connections are closed on success and failure paths. 8. Confirm that retry loops have finite limits. 9. Confirm that generated result files contain the required fields. 10. Report pass or fail with exact command results, changed files, test counts, and detected risks. Do not report success if any required command was not executed.
The value of the skill is not that it contains complicated logic. Its value is that the same review procedure is applied every time.
Without a skill, one session may run tests but forget the diff. Another may inspect the diff but never connect to a real browser. A third may accept a successful process exit even though the extracted result is empty.
What the BrowserAPI smoke test should prove
A smoke test should be small, stable, and owned by the project.
Do not use an arbitrary third-party website as the only verification target. External pages change, block requests, or return different content depending on location.
A useful smoke test can:
- connect to MegaIndex BrowserAPI;
- open a controlled test page;
- wait for a known element;
- execute a small JavaScript interaction;
- verify the resulting DOM state;
- save a screenshot;
- return structured output;
- close the browser connection.
Example expected result:
{
"status": "passed",
"finalUrl": "https://staging.example.com/browser-test",
"heading": "Browser test page",
"interactionCompleted": true,
"consoleErrors": [],
"screenshot": "artifacts/browserapi-smoke.png"
}The test should fail when:
- the browser cannot connect;
- navigation times out;
- the expected element is missing;
- the interaction does not change the page;
- the result object is incomplete;
- the browser session cannot be closed cleanly.
A process exit code of zero is not enough if the script quietly returns an empty object.
Add a Stop hook so Claude cannot finish on failing tests
A skill tells Claude to run the verification procedure. A Stop hook makes the verification unavoidable.
The hook runs when Claude attempts to finish its turn. If verification fails, the hook blocks completion and returns the failure to the agent.
A simple verification script may look like this:
#!/usr/bin/env bash set -uo pipefail FAILED=0 echo "Running unit tests..." npm test || FAILED=1 echo "Running type checking..." npm run typecheck || FAILED=1 echo "Running BrowserAPI smoke test..." npm run test:browserapi || FAILED=1 echo "Checking diff formatting..." git diff --check || FAILED=1 echo "Checking for newly skipped tests..." if git diff --unified=0 -- '*.ts' '*.tsx' '*.js' '*.mjs' \ | grep -E '^\+.*\b(test|it|describe)\.skip\b|^\+.*\b(xit|xdescribe)\b' then echo "New skipped tests detected." FAILED=1 fi echo "Checking for possible BrowserAPI secrets..." if git diff --unified=0 \ | grep -E '^\+.*(ws://|wss://).+@|^\+.*(proxy_password|browser_password|authorization).*=.+' then echo "Possible credentials detected in the diff." FAILED=1 fi if [ "$FAILED" -ne 0 ]; then echo "Verification failed. Claude must continue working." exit 2 fi echo "Verification passed." exit 0
The critical detail is the exit code.
Exit code 1 is not the blocking signal for a Claude Code hook. Use exit code 2 when the hook must stop completion.
This is an easy mistake to make. A script may appear to fail in the terminal while Claude is still allowed to continue because the hook returned the wrong code.
Why auto mode is not enough
Auto mode is useful when Claude should work with fewer approval prompts. It includes a safety classifier that evaluates the intent of actions.
That can help detect actions such as:
- deploying to production unexpectedly;
- force-pushing a branch;
- sending sensitive data to an external endpoint;
- running destructive commands outside the task scope.
It does not determine whether the implementation is correct.
A broken BrowserAPI change is not necessarily dangerous. The classifier may allow it because incorrect selectors, missing cleanup, or an empty extraction result do not look like security violations.
The reliable combination is:
| Mechanism | What it checks |
|---|---|
| Auto mode classifier | Whether the attempted action appears unsafe or exceeds the request |
| Stop hook | Whether tests, type checks, BrowserAPI verification, and project-specific requirements pass |
The classifier checks intent. The hook checks evidence.
Use plan mode before large BrowserAPI changes
Plan mode is most useful when a task touches several parts of the browser infrastructure.
Examples include:
- adding persistent browser profiles;
- introducing proxy selection per task;
- changing session reuse logic;
- adding parallel browser execution;
- moving from one result format to another;
- building resumable data collection;
- adding billing or traffic limits.
A useful request is:
Enter plan mode. Inspect the BrowserAPI integration and prepare a plan for adding persistent browser profiles. The plan must identify: - all files that will change; - how profiles are created and selected; - how concurrent access to one profile is prevented; - how browser sessions are closed; - how failures are retried; - which tests must be added or updated; - how the change will be verified against MegaIndex BrowserAPI. Do not modify files until the plan has been reviewed.
Reading the plan matters. The point is not to make Claude produce another document. The point is to catch an incorrect architecture before it spreads across several files.
For example, the plan may reveal that Claude intends to create a new remote browser connection for every page instead of reusing one session. Correcting that in the plan is cheaper than fixing the implementation later.
Do not treat /goal as proof
The /goal command can keep Claude working until a completion condition appears to be satisfied.
For example:
/goal BrowserAPI tests pass, no type errors remain, and all browser sessions close cleanly
This is useful for keeping a long task moving, but it is not a verification boundary.
The evaluator reads the transcript. It can see command output that Claude produced, but it does not independently inspect the process, repository, browser session, or test environment.
A convincing transcript can therefore satisfy the goal even when the underlying work is incomplete.
The safer model is:
- use /goal to describe when Claude should stop iterating;
- use a hook to execute the actual checks;
- let the hook block completion when those checks fail.
The goal describes “done.” The hook proves it.
Keep long sessions under control
BrowserAPI work can produce long Claude Code sessions, especially when the agent is fixing selectors, investigating navigation failures, or changing several modules.
Three tools are useful here.
Plan before editing
Use plan mode for architectural work and multi-file changes.
Compact with a focus
A generic compaction may remove details that matter later.
Instead of using only:
/compact
Add the context that must survive:
/compact Focus on the BrowserAPI profile lifecycle, session cleanup rules, the failing integration test, and the files changed in src/browser/.
This reduces the chance that Claude forgets why a particular implementation decision was made.
Rewind instead of arguing with a bad direction
If Claude takes the wrong architectural path, repeatedly prompting it to repair the same approach often creates more complexity.
Return to a checkpoint before the wrong decision, preserve only the useful discussion, and restart from a cleaner state.
This is especially useful when Claude:
- replaces a shared connection manager with duplicated connection code;
- mixes profile storage with task progress;
- adds retries at several layers;
- rewrites tests around the implementation instead of the required behavior.
Use worktrees when BrowserAPI tasks run in parallel
Parallel Claude sessions can conflict when they modify the same repository.
For example:
- one session changes browser connection handling;
- another adds proxy configuration;
- a third updates Playwright tests.
Git worktrees give each session an independent working tree while preserving the same repository history.
Each session should also use separate:
- output directories;
- test artifacts;
- browser profiles;
- task state files.
A useful additional file is:
.worktreeinclude
It can list ignored local files that must be copied into each worktree, such as a development environment file or local test configuration.
Do not use the same browser profile for unrelated parallel tests. Shared cookies, open tabs, or account state can make one test affect another.
Use structured output for headless reviews later
For an early-stage project, interactive Claude Code plus hooks is usually enough.
Headless execution becomes useful when browser code reviews need to run from scripts or CI.
Instead of returning a free-form review such as:
The change looks good. I found one minor risk.
Claude can return a structured result:
{
"verdict": "fail",
"filesChanged": [
"src/browser/connection.ts",
"tests/browserapi/session.test.ts"
],
"tests": {
"unit": "passed",
"typecheck": "passed",
"browserapiSmoke": "failed"
},
"risks": [
{
"severity": "high",
"file": "src/browser/connection.ts",
"issue": "Browser connection is not closed when page.goto throws"
}
]
}Structured output can be:
- stored as a CI artifact;
- filtered with jq;
- used to fail a pipeline;
- saved for later review;
- compared between runs.
This is valuable after the basic development workflow is stable. It does not need to be the first layer added to an MVP.
What to implement now and what to postpone
Not every Claude Code feature is equally useful at the beginning of a BrowserAPI project.
| Feature | Decision | Reason |
|---|---|---|
| Lean CLAUDE.md | Implement now | Immediately improves the quality and consistency of code changes |
| BrowserAPI verification skill | Implement now | Makes the review procedure repeatable |
| Stop hook | Implement now | Prevents Claude from finishing when mechanical checks fail |
| Plan mode | Use for large changes | Reduces expensive architectural mistakes |
| Focused /compact and rewind | Use as needed | Helps control long debugging and refactoring sessions |
| Worktrees | Postpone until parallel work begins | Adds value only when several sessions work at the same time |
| /goal | Use carefully | Useful for iteration, but not a substitute for verification |
| Headless structured output | Add after the MVP | Useful for automated reviews and CI reporting |
| Routines | Postpone | Helpful for recurring audits, but unnecessary for the first development loop |
| Plugins and Agent SDK | Skip initially | They add packaging and integration complexity without solving the immediate verification problem |
Common mistakes
Using exit code 1 in a blocking hook
The hook logs the error, but Claude may still continue. Use exit code 2 when the action or completion must be blocked.
Running with bypass permissions outside isolation
Bypass mode removes the safety checks. It should not be used on a normal development machine with access to real credentials and repositories.
Auto mode should be the upper limit for unattended work unless the entire environment is disposable and isolated.
Trusting /goal without an external check
A completion condition based on transcript output can be satisfied by convincing text. Tests and BrowserAPI verification must be executed by a hook or another deterministic process.
Allowing updated input to drop fields
When a hook rewrites tool input, the updated object replaces the complete input rather than modifying one field. Every required field must be returned.
Using imports to “reduce” CLAUDE.md context
Imports help organize instructions, but imported content still enters the context. Split files for ownership and readability, not as a context-saving technique.
Installing plugins without reading their hooks
A plugin may add hooks, agents, and MCP configurations that run with local permissions. Automated review does not make third-party code trustworthy.
Plugins are not necessary for establishing the first reliable BrowserAPI workflow.
A practical first implementation
The first version does not need a large autonomous platform.
Start with three changes.
1. Revise CLAUDE.md
Keep only concrete project conventions:
- where BrowserAPI code belongs;
- how the endpoint is loaded;
- how sessions are closed;
- how retries are limited;
- how results are structured;
- which tests must run;
- which actions are prohibited.
Delete generic rules that cannot be checked.
2. Add verify-browserapi skill
Make it responsible for:
- unit tests;
- type checking;
- the remote-browser smoke test;
- full diff review;
- test-strength review;
- secret detection;
- session-cleanup review;
- an evidence-based final report.
3. Add a blocking Stop hook
The hook should:
- run the required commands;
- detect newly skipped tests;
- scan the diff for credentials;
- return exit code 2 when verification fails;
- feed the failure back to Claude so it can continue fixing the task.
This is enough to change the development model.
Claude is no longer trusted because it produced a clean summary. It is trusted only after the project’s own checks allow it to finish.
Conclusion
The most useful Claude Code setup for MegaIndex BrowserAPI development is not the one with the most autonomous features.
It is the one that clearly separates:
- instructions Claude should follow;
- procedures Claude should repeat;
- checks Claude cannot bypass.
Use CLAUDE.md for concise project conventions. Use a verification skill for the browser-specific review procedure. Use hooks for tests, credential checks, session cleanup, and other requirements that must hold before the work is accepted.
Plan mode, worktrees, structured output, and recurring routines can be added as the project grows. Plugins and the Agent SDK can wait until there is a real need to package or embed the workflow.
For an early BrowserAPI project, the priority is simpler: make “done” something the code proves, not something the agent says.
Discussion