The Architecture of Agency: Files, Prompts, and the User Boundary
The important question in career-ops is not only what the AI can do. It is who owns the evidence, the records, and the decisions after the AI has done it. This chapter looks at the repository's answer: a two-layer workspace in which system logic can evolve, personal data remains protected, and derived indexes never replace human-readable records. Understanding that boundary lets you predict what an AI provider sees, what an update may change, and why the system can remain candidate-controlled while still using automation.
Ownership is a runtime property
career-ops divides its workspace into a system layer and a user layer. The system layer contains the machinery that defines the workflow: Markdown modes, Node utilities, templates, provider modules, documentation, and the files that let different coding CLIs enter the same command center. The user layer contains the candidate's CV, profile, proof points, reports, saved job descriptions, tracker, pipeline, interview material, generated output, and personal configuration.
That separation is not just an organizational preference. It answers a practical question about updates. System files are expected to change as the project adds a new scanner, improves a fact check, or corrects a prompt. A CV or an application record is different: it is the user's work product. An updater that treated both categories alike could improve the software by deleting the history that makes it useful. The data contract therefore declares which files the update process may replace and which it must never touch.
A third category sits between code and personal evidence: derived state. The tracker and reports are readable records that a person can inspect directly. A database or other index may accelerate searches and dashboards, but it is rebuildable from those records. This is a consequential design choice. If the index becomes the real source of truth, a user can lose the explanation behind a decision when the schema changes, a synchronization job fails, or a different tool reads the same work history. Keeping the Markdown files canonical makes the workflow legible to both humans and small scripts.
The layers can be summarized without turning the repository into an inventory:
| Layer | Owns | Why it matters |
|---|---|---|
| System | Workflow logic, prompts, scripts, templates, and providers | Can be refreshed as the implementation evolves |
| User | CV, profile, reports, pipeline, tracker, and personal settings | Carries the candidate's evidence and decisions |
| Derived | Search indexes and dashboard views built from user records | Improves speed without becoming a second truth |
Consider the record of one application. A compact tracker row points to a full report. The report explains the match and preserves the job description that was available at the time. A generated CV may be linked to the same report, while a dashboard index makes the row easy to filter. If the index disappears, the row, report, and document still explain the decision. If the report disappears, a fast query over an index cannot reconstruct the reasoning that produced it. The direction of dependence is therefore intentional: convenience reads from the record; the record does not depend on convenience.
This ownership model also limits what a mode is allowed to infer. The evaluation prompt can use a profile to understand a candidate's target roles, but it cannot quietly turn a system default into a personal fact. A report can store a judgment about a posting, but it should not overwrite the candidate's source CV. Agency begins with these mundane distinctions because an automated decision is only reviewable when its inputs and durable consequences have an owner.
Why the readable files stay canonical
The repository calls its Markdown records permanent source of truth because they are portable, diffable, and available to many readers. The web interface, terminal dashboard, plugins, and community scripts can all read a plain tracker or report without sharing one database driver. This avoids a familiar systems failure: two stores begin with the same data, then one is updated while the other is not, and nobody knows which version controls the next action.
A report is more than a score card. It preserves the role's reasoning, the evidence used to match requirements, the legitimacy signals, the next action, and a verbatim job-description archive. The archive matters because a URL is a pointer to a moving external page. A posting can close or change after an application, while the candidate still needs to understand what they were evaluated against during an interview. The report keeps that context close to the decision rather than relying on the employer's page to remain available.
The tracker serves a different scale of reading. It is the compact index of the search: company, role, score, status, report, PDF state, and notes. Because it is canonical, the repository does not ask a collection of parallel workers to edit it directly. Workers write a headed tracker addition, and a merge process validates the fields, resolves duplicates, normalizes links, and acquires a lock before updating the table. This is a small but important interface: generation produces a proposal; the canonical writer decides how that proposal enters shared state.
The arrangement has costs. Plain files are less efficient for some queries, and compatibility with older tracker shapes adds validation logic. A derived SQLite index would be faster for filtering and sorting, which is why the architecture permits one. But speed belongs on the derived side. The system chooses the extra work of rebuilding an index over the permanent ambiguity of maintaining two authoritative stores.
That choice also affects how failures are diagnosed. A missing dashboard result does not necessarily mean that an application vanished; the user can inspect the tracker and rebuild the index. A duplicate tracker row is not merely a display glitch, because the canonical table can change downstream counts, follow-up reminders, and outcome analysis. Conversely, the existence of a machine-readable summary does not make its judgments objective. Stable fields make records easier to process, not more certain than the evidence behind them.
The AI sits at the edge of the workspace
The repository does not hard-code one model as the owner of the workflow. Its modes can be loaded by several AI coding CLIs, and standalone scripts can route evaluation to local or OpenAI-compatible endpoints. That portability comes from keeping the rules in readable prompt files and keeping repeatable operations in ordinary programs. A selected CLI interprets a job description against a profile; a script can normalize a URL, merge a TSV row, render a PDF, or check a generated document's facts.
This is what AI-agnostic means here: the workflow's interfaces are shared even when the model, authentication method, and quality vary. It does not mean that every model will produce the same judgment. The repository offers spend tiers precisely because reasoning quality, latency, and cost remain different choices. A model can also fail to follow a structured report or misread a qualitative requirement. The file contract makes those failures easier to inspect, but it cannot make them impossible.
Nor does local-first mean that all processing stays on the device. The repository can keep the candidate's files in a local checkout and avoid a hosted career-ops database. When the candidate invokes a remote AI provider, however, the CV, profile, job description, and relevant reports may cross that provider boundary as model context. The user controls the choice of provider, not the provider's internal retention or processing policies. A local checkout reduces one category of exposure; it does not remove the need to review the service receiving the prompt.
The data root makes that distinction operational. User data can resolve to a directory selected by an environment variable, a marker file in the repository, or the repository itself as a default. The code can therefore be updated in one place while a CV, reports, and tracker live elsewhere. The same resolver is used for tracker reads and writes, which prevents one command from looking in an external data directory while another silently creates a new tracker in the code checkout. Portability here is not a promise that data syncs automatically. It is a way to choose where the data lives and keep the tools pointed at that choice.
The edge model creates a useful division of responsibility. The AI is suited to interpretation: deciding which parts of a long posting matter, relating a real achievement to a role, or explaining an ambiguous trade-off. Deterministic utilities are suited to invariants: a URL should be normalized consistently, a report link should point to a file, and an unsupported metric should fail a fact check. If those responsibilities are reversed, the system becomes harder to trust. A model asked to maintain every table invariant may improvise; a script asked to judge a candidate's motivation may reduce a human question to a brittle rule.
The diagram's most important arrow is the dotted one. The updater can change the machinery, but it is not the owner of the candidate's records. The arrow from user data to the AI provider is equally important because it prevents a comforting but inaccurate reading of local-first: the workspace is local, while model processing may not be.
Updating without erasing the search
A self-updater could have been implemented as an ordinary pull, but the repository treats it as a constrained ownership operation. It maintains an explicit list of system paths and a separate list of user paths. The updater checks out the system side from upstream, while excluding the CV, profile, personal modes, data, reports, output, saved JDs, plugins, and settings that belong to the user.
Forks create a complication. A user may add a private runner or a local integration that upstream does not know exists. The updater allows a separate local-path declaration for such files, but validates it before trusting it. Absolute paths and traversal are rejected because they would extend the protection boundary outside the checkout. A declaration that collides with a system path is also rejected because it would silently freeze a file that is supposed to receive updates. Even path spelling is constrained: an ambiguous or non-canonical spelling could look protected in one check and unprotected in another.
The difficult case is a local edit to a system file. Perhaps the candidate changed a scoring rule or a provider while waiting for the upstream project to adopt the idea. A raw system checkout would overwrite that work. The updater detects files changed locally and changed differently upstream, writes a backup, and by default preserves the local version while explaining that it will not receive upstream changes. A force path exists, but it is explicit, and the backup makes the destructive choice recoverable. This is not merge automation. It is a visible choice between receiving future system changes and retaining a local modification.
That choice introduces a maintenance cost. A preserved system edit can drift from the project's current behavior, and the candidate must eventually decide whether to retire it, reapply it, or carry it forward. The design accepts that cost because silent loss would be worse. It also treats the ignore file carefully: a user may have added rules to keep personal data out of Git, so upstream ignore rules are reconciled rather than blindly replacing local ones.
The result is a useful test for any claimed candidate-control system: can a person identify the boundary, move their personal records, update the machinery, and recover from a conflict without guessing which files were changed? In career-ops, the answer is intended to be yes, provided the user keeps data inside declared paths and uses the normal writers. The architecture does not eliminate care; it makes the places where care is needed explicit.
That explicitness prepares the next problem. Once the workspace can preserve a candidate's evidence and decisions, the system still needs a way to find promising roles without asking a model to read every posting. The discovery layer applies the same ownership principle to attention: cheap, inspectable filters first, expensive reasoning later.